From 5f9ca7a57c0523ae7706488271996deb642211cb Mon Sep 17 00:00:00 2001 From: Bryce Neal Date: Mon, 16 May 2016 12:59:32 -0700 Subject: [PATCH 01/10] Use different heuristics based on 4 vs 8 directions --- src/easystar.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/easystar.js b/src/easystar.js index 56dbaa6..80563e3 100755 --- a/src/easystar.js +++ b/src/easystar.js @@ -458,6 +458,20 @@ EasyStar.js = function() { }; var getDistance = function(x1,y1,x2,y2) { - return Math.sqrt( (x2-=x1)*x2 + (y2-=y1)*y2 ); + if (diagonalsEnabled) { + // Octile distance + var dx = Math.abs(x1 - x2); + var dy = Math.abs(y1 - y2); + if (dx < dy) { + return DIAGONAL_COST * dx + dy; + } else { + return DIAGONAL_COST * dy + dx; + } + } else { + // Manhattan distance + var dx = Math.abs(x1 - x2); + var dy = Math.abs(y1 - y2); + return (dx + dy) + } }; } \ No newline at end of file From 9e3ce46bd2ec6c7902a3a1815d9d240802e9c147 Mon Sep 17 00:00:00 2001 From: Bryce Neal Date: Mon, 16 May 2016 17:47:17 -0700 Subject: [PATCH 02/10] Use heap module --- bin/easystar-0.2.3.js | 566 +++++++++++++++++++++++++++----------- bin/easystar-0.2.3.min.js | 2 +- gulpfile.js | 2 +- package.json | 4 +- src/easystar.js | 20 +- src/priority-queue.js | 153 ----------- test/priorityqueuetest.js | 73 ----- 7 files changed, 416 insertions(+), 404 deletions(-) mode change 100644 => 100755 bin/easystar-0.2.3.js mode change 100644 => 100755 bin/easystar-0.2.3.min.js delete mode 100755 src/priority-queue.js delete mode 100644 test/priorityqueuetest.js diff --git a/bin/easystar-0.2.3.js b/bin/easystar-0.2.3.js old mode 100644 new mode 100755 index 16c9139..2deaf30 --- a/bin/easystar-0.2.3.js +++ b/bin/easystar-0.2.3.js @@ -1,3 +1,379 @@ +// 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); + // NameSpace var EasyStar = EasyStar || {}; @@ -38,160 +414,6 @@ EasyStar.Node = function(parent, x, y, costSoFar, simpleDistanceToTarget) { // Constants EasyStar.Node.OPEN_LIST = 0; EasyStar.Node.CLOSED_LIST = 1; -/** -* This is an improved Priority Queue data type implementation that can be used to sort any object type. -* It uses a technique called a binary heap. -* -* For more on binary heaps see: http://en.wikipedia.org/wiki/Binary_heap -* -* @param {String} criteria The criteria by which to sort the objects. -* This should be a property of the objects you're sorting. -* -* @param {Number} heapType either PriorityQueue.MAX_HEAP or PriorityQueue.MIN_HEAP. -**/ -EasyStar.PriorityQueue = function(criteria,heapType) { - this.length = 0; //The current length of heap. - var queue = []; - var isMax = false; - - //Constructor - if (heapType==EasyStar.PriorityQueue.MAX_HEAP) { - isMax = true; - } else if (heapType==EasyStar.PriorityQueue.MIN_HEAP) { - isMax = false; - } else { - throw heapType + " not supported."; - } - - /** - * Inserts the value into the heap and sorts it. - * - * @param value The object to insert into the heap. - **/ - this.insert = function(value) { - if (!value.hasOwnProperty(criteria)) { - throw "Cannot insert " + value + " because it does not have a property by the name of " + criteria + "."; - } - queue.push(value); - this.length++; - bubbleUp(this.length-1); - } - - /** - * Peeks at the highest priority element. - * - * @return the highest priority element - **/ - this.getHighestPriorityElement = function() { - return queue[0]; - } - - /** - * Removes and returns the highest priority element from the queue. - * - * @return the highest priority element - **/ - this.shiftHighestPriorityElement = function() { - if (this.length === 0) { - throw ("There are no more elements in your priority queue."); - } else if (this.length === 1) { - var onlyValue = queue[0]; - queue = []; - this.length = 0; - return onlyValue; - } - var oldRoot = queue[0]; - var newRoot = queue.pop(); - this.length--; - queue[0] = newRoot; - swapUntilQueueIsCorrect(0); - return oldRoot; - } - - var bubbleUp = function(index) { - if (index===0) { - return; - } - var parent = getParentOf(index); - if (evaluate(index,parent)) { - swap(index,parent); - bubbleUp(parent); - } else { - return; - } - } - - var swapUntilQueueIsCorrect = function(value) { - var left = getLeftOf(value); - var right = getRightOf(value); - if (evaluate(left,value)) { - swap(value,left); - swapUntilQueueIsCorrect(left); - } else if (evaluate(right,value)) { - swap(value,right); - swapUntilQueueIsCorrect(right); - } else if (value==0) { - return; - } else { - swapUntilQueueIsCorrect(0); - } - } - - var swap = function(self,target) { - var placeHolder = queue[self]; - queue[self] = queue[target]; - queue[target] = placeHolder; - } - - var evaluate = function(self,target) { - if (queue[target]===undefined||queue[self]===undefined) { - return false; - } - - var selfValue; - var targetValue; - - // Check if the criteria should be the result of a function call. - if (typeof queue[self][criteria] === 'function') { - selfValue = queue[self][criteria](); - targetValue = queue[target][criteria](); - } else { - selfValue = queue[self][criteria]; - targetValue = queue[target][criteria]; - } - - if (isMax) { - if (selfValue > targetValue) { - return true; - } else { - return false; - } - } else { - if (selfValue < targetValue) { - return true; - } else { - return false; - } - } - } - - var getParentOf = function(index) { - return Math.floor((index-1) / 2); - } - - var getLeftOf = function(index) { - return index*2 + 1; - } - - var getRightOf = function(index) { - return index*2 + 2; - } -}; - -// Constants -EasyStar.PriorityQueue.MAX_HEAP = 0; -EasyStar.PriorityQueue.MIN_HEAP = 1; - /** * Represents a single instance of EasyStar. * A path that is in the queue to eventually be found. @@ -449,7 +671,9 @@ EasyStar.js = function() { // Create the instance var instance = new EasyStar.instance(); - instance.openList = new EasyStar.PriorityQueue("bestGuessDistance",EasyStar.PriorityQueue.MIN_HEAP); + instance.openList = new Heap(function(nodeA, nodeB) { + return nodeA.bestGuessDistance() - nodeB.bestGuessDistance(); + }) instance.isDoneCalculating = false; instance.nodeHash = {}; instance.startX = startX; @@ -458,7 +682,7 @@ EasyStar.js = function() { instance.endY = endY; instance.callback = callbackWrapper; - instance.openList.insert(coordinateToNode(instance, instance.startX, + instance.openList.push(coordinateToNode(instance, instance.startX, instance.startY, null, STRAIGHT_COST)); instances.push(instance); @@ -485,14 +709,14 @@ EasyStar.js = function() { } // Couldn't find a path. - if (instances[0].openList.length === 0) { + if (instances[0].openList.size() === 0) { var ic = instances[0]; ic.callback(null); instances.shift(); continue; } - var searchNode = instances[0].openList.shiftHighestPriorityElement(); + var searchNode = instances[0].openList.pop(); var tilesToSearch = []; searchNode.list = EasyStar.Node.CLOSED_LIST; @@ -625,12 +849,10 @@ EasyStar.js = function() { if (node.list === undefined) { node.list = EasyStar.Node.OPEN_LIST; - instance.openList.insert(node); - } else if (node.list === EasyStar.Node.OPEN_LIST) { - if (searchNode.costSoFar + cost < node.costSoFar) { - node.costSoFar = searchNode.costSoFar + cost; - node.parent = searchNode; - } + instance.openList.push(node); + } else if (searchNode.costSoFar + cost < node.costSoFar) { + node.costSoFar = searchNode.costSoFar + cost; + instance.openList.updateItem(node); } } } @@ -667,6 +889,20 @@ EasyStar.js = function() { }; var getDistance = function(x1,y1,x2,y2) { - return Math.sqrt( (x2-=x1)*x2 + (y2-=y1)*y2 ); + if (diagonalsEnabled) { + // Octile distance + var dx = Math.abs(x1 - x2); + var dy = Math.abs(y1 - y2); + if (dx < dy) { + return DIAGONAL_COST * dx + dy; + } else { + return DIAGONAL_COST * dy + dx; + } + } else { + // Manhattan distance + var dx = Math.abs(x1 - x2); + var dy = Math.abs(y1 - y2); + return (dx + dy) + } }; } \ No newline at end of file diff --git a/bin/easystar-0.2.3.min.js b/bin/easystar-0.2.3.min.js old mode 100644 new mode 100755 index c2e44bf..813b449 --- a/bin/easystar-0.2.3.min.js +++ b/bin/easystar-0.2.3.min.js @@ -1 +1 @@ -var EasyStar=EasyStar||{};"function"==typeof define&&define.amd&&define("easystar",[],function(){return EasyStar}),"undefined"!=typeof module&&module.exports&&(module.exports=EasyStar),EasyStar.Node=function(t,n,e,i,s){this.parent=t,this.x=n,this.y=e,this.costSoFar=i,this.simpleDistanceToTarget=s,this.bestGuessDistance=function(){return this.costSoFar+this.simpleDistanceToTarget}},EasyStar.Node.OPEN_LIST=0,EasyStar.Node.CLOSED_LIST=1,EasyStar.PriorityQueue=function(t,n){this.length=0;var e=[],i=!1;if(n==EasyStar.PriorityQueue.MAX_HEAP)i=!0;else{if(n!=EasyStar.PriorityQueue.MIN_HEAP)throw n+" not supported.";i=!1}this.insert=function(n){if(!n.hasOwnProperty(t))throw"Cannot insert "+n+" because it does not have a property by the name of "+t+".";e.push(n),this.length++,s(this.length-1)},this.getHighestPriorityElement=function(){return e[0]},this.shiftHighestPriorityElement=function(){if(0===this.length)throw"There are no more elements in your priority queue.";if(1===this.length){var t=e[0];return e=[],this.length=0,t}var n=e[0],i=e.pop();return this.length--,e[0]=i,o(0),n};var s=function(t){if(0!==t){var n=u(t);a(t,n)&&(r(t,n),s(n))}},o=function(t){var n=h(t),e=c(t);if(a(n,t))r(t,n),o(n);else if(a(e,t))r(t,e),o(e);else{if(0==t)return;o(0)}},r=function(t,n){var i=e[t];e[t]=e[n],e[n]=i},a=function(n,s){if(void 0===e[s]||void 0===e[n])return!1;var o,r;return"function"==typeof e[n][t]?(o=e[n][t](),r=e[s][t]()):(o=e[n][t],r=e[s][t]),i?o>r?!0:!1:r>o?!0:!1},u=function(t){return Math.floor((t-1)/2)},h=function(t){return 2*t+1},c=function(t){return 2*t+2}},EasyStar.PriorityQueue.MAX_HEAP=0,EasyStar.PriorityQueue.MIN_HEAP=1,EasyStar.instance=function(){this.isDoneCalculating=!0,this.pointsToAvoid={},this.startX,this.callback,this.startY,this.endX,this.endY,this.nodeHash={},this.openList},EasyStar.js=function(){var t,n,e,i=1,s=1.4,o=!1,r={},a={},u={},h=!0,c=[],l=Number.MAX_VALUE,f=!1;this.setAcceptableTiles=function(t){t instanceof Array?e=t:!isNaN(parseFloat(t))&&isFinite(t)&&(e=[t])},this.enableSync=function(){o=!0},this.disableSync=function(){o=!1},this.enableDiagonals=function(){f=!0},this.disableDiagonals=function(){f=!1},this.setGrid=function(n){t=n;for(var e=0;en||0>s||0>r||0>a||n>t[0].length-1||s>t.length-1||r>t[0].length-1||a>t.length-1)throw new Error("Your start or end point is outside the scope of your grid.");if(n===r&&s===a)return h([]),void 0;for(var l=t[a][r],f=!1,y=0;yn;n++){if(0===c.length)return;if(o&&(n=0),0!==c[0].openList.length){var r=c[0].openList.shiftHighestPriorityElement(),a=[];r.list=EasyStar.Node.CLOSED_LIST,r.y>0&&a.push({instance:c[0],searchNode:r,x:0,y:-1,cost:i*v(r.x,r.y-1)}),r.x0&&a.push({instance:c[0],searchNode:r,x:-1,y:0,cost:i*v(r.x-1,r.y)}),f&&(r.x>0&&r.y>0&&(h||d(t,e,r.x,r.y-1)&&d(t,e,r.x-1,r.y))&&a.push({instance:c[0],searchNode:r,x:-1,y:-1,cost:s*v(r.x-1,r.y-1)}),r.x0&&(h||d(t,e,r.x,r.y-1)&&d(t,e,r.x+1,r.y))&&a.push({instance:c[0],searchNode:r,x:1,y:-1,cost:s*v(r.x+1,r.y-1)}),r.x>0&&r.ye?-1:e===i?0:1});for(var u=!1,p=0;pt?-1:t>n?1:0},u=function(t,o,i,r,s){var a;if(null==i&&(i=0),null==s&&(s=n),0>i)throw new Error("lo must be non-negative");for(null==r&&(r=t.length);r>i;)a=e((i+r)/2),s(o,t[a])<0?r=a:i=a+1;return[].splice.apply(t,[i,i-i].concat(o)),o},r=function(t,e,o){return null==o&&(o=n),t.push(e),f(t,0,t.length-1,o)},i=function(t,e){var o,i;return null==e&&(e=n),o=t.pop(),t.length?(i=t[0],t[0]=o,d(t,0,e)):i=o,i},a=function(t,e,o){var i;return null==o&&(o=n),i=t[0],t[0]=e,d(t,0,o),i},s=function(t,e,o){var i;return null==o&&(o=n),t.length&&o(t[0],e)<0&&(i=[t[0],e],e=i[0],t[0]=i[1],d(t,0,o)),e},o=function(t,o){var i,r,s,a,u,c;for(null==o&&(o=n),a=function(){c=[];for(var n=0,o=e(t.length/2);o>=0?o>n:n>o;o>=0?n++:n--)c.push(n);return c}.apply(this).reverse(),u=[],r=0,s=a.length;s>r;r++)i=a[r],u.push(d(t,i,o));return u},p=function(t,e,o){var i;return null==o&&(o=n),i=t.indexOf(e),-1!==i?(f(t,0,i,o),d(t,i,o)):void 0},l=function(t,e,i){var r,a,u,c,l;if(null==i&&(i=n),a=t.slice(0,e),!a.length)return a;for(o(a,i),l=t.slice(e),u=0,c=l.length;c>u;u++)r=l[u],s(a,r,i);return a.sort(i).reverse()},h=function(t,e,r){var s,a,l,h,p,f,d,y,v,g;if(null==r&&(r=n),10*e<=t.length){if(h=t.slice(0,e).sort(r),!h.length)return h;for(l=h[h.length-1],y=t.slice(e),p=0,d=y.length;d>p;p++)s=y[p],r(s,l)<0&&(u(h,s,0,null,r),h.pop(),l=h[h.length-1]);return h}for(o(t,r),g=[],a=f=0,v=c(e,t.length);v>=0?v>f:f>v;a=v>=0?++f:--f)g.push(i(t,r));return g},f=function(t,e,o,i){var r,s,a;for(null==i&&(i=n),r=t[o];o>e&&(a=o-1>>1,s=t[a],i(r,s)<0);)t[o]=s,o=a;return t[o]=r},d=function(t,e,o){var i,r,s,a,u;for(null==o&&(o=n),r=t.length,u=e,s=t[e],i=2*e+1;r>i;)a=i+1,r>a&&!(o(t[i],t[a])<0)&&(i=a),t[e]=t[i],e=i,i=2*e+1;return t[e]=s,f(t,u,e,o)},t=function(){function t(t){this.cmp=null!=t?t:n,this.nodes=[]}return t.push=r,t.pop=i,t.replace=a,t.pushpop=s,t.heapify=o,t.updateItem=p,t.nlargest=l,t.nsmallest=h,t.prototype.push=function(t){return r(this.nodes,t,this.cmp)},t.prototype.pop=function(){return i(this.nodes,this.cmp)},t.prototype.peek=function(){return this.nodes[0]},t.prototype.contains=function(t){return-1!==this.nodes.indexOf(t)},t.prototype.replace=function(t){return a(this.nodes,t,this.cmp)},t.prototype.pushpop=function(t){return s(this.nodes,t,this.cmp)},t.prototype.heapify=function(){return o(this.nodes,this.cmp)},t.prototype.updateItem=function(t){return p(this.nodes,t,this.cmp)},t.prototype.clear=function(){return this.nodes=[]},t.prototype.empty=function(){return 0===this.nodes.length},t.prototype.size=function(){return this.nodes.length},t.prototype.clone=function(){var n;return n=new t,n.nodes=this.nodes.slice(0),n},t.prototype.toArray=function(){return this.nodes.slice(0)},t.prototype.insert=t.prototype.push,t.prototype.top=t.prototype.peek,t.prototype.front=t.prototype.peek,t.prototype.has=t.prototype.contains,t.prototype.copy=t.prototype.clone,t}(),function(t,n){return"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?module.exports=n():t.Heap=n()}(this,function(){return t})}).call(this);var EasyStar=EasyStar||{};"function"==typeof define&&define.amd&&define("easystar",[],function(){return EasyStar}),"undefined"!=typeof module&&module.exports&&(module.exports=EasyStar),EasyStar.Node=function(t,n,e,o,i){this.parent=t,this.x=n,this.y=e,this.costSoFar=o,this.simpleDistanceToTarget=i,this.bestGuessDistance=function(){return this.costSoFar+this.simpleDistanceToTarget}},EasyStar.Node.OPEN_LIST=0,EasyStar.Node.CLOSED_LIST=1,EasyStar.instance=function(){this.isDoneCalculating=!0,this.pointsToAvoid={},this.startX,this.callback,this.startY,this.endX,this.endY,this.nodeHash={},this.openList},EasyStar.js=function(){var t,n,e,o=1,i=1.4,r=!1,s={},a={},u={},c=!0,l=[],h=Number.MAX_VALUE,p=!1;this.setAcceptableTiles=function(t){t instanceof Array?e=t:!isNaN(parseFloat(t))&&isFinite(t)&&(e=[t])},this.enableSync=function(){r=!0},this.disableSync=function(){r=!1},this.enableDiagonals=function(){p=!0},this.disableDiagonals=function(){p=!1},this.setGrid=function(n){t=n;for(var e=0;en||0>i||0>s||0>a||n>t[0].length-1||i>t.length-1||s>t[0].length-1||a>t.length-1)throw new Error("Your start or end point is outside the scope of your grid.");if(n===s&&i===a)return c([]),void 0;for(var h=t[a][s],p=!1,f=0;fn;n++){if(0===l.length)return;if(r&&(n=0),0!==l[0].openList.size()){var s=l[0].openList.pop(),a=[];s.list=EasyStar.Node.CLOSED_LIST,s.y>0&&a.push({instance:l[0],searchNode:s,x:0,y:-1,cost:o*y(s.x,s.y-1)}),s.x0&&a.push({instance:l[0],searchNode:s,x:-1,y:0,cost:o*y(s.x-1,s.y)}),p&&(s.x>0&&s.y>0&&(c||d(t,e,s.x,s.y-1)&&d(t,e,s.x-1,s.y))&&a.push({instance:l[0],searchNode:s,x:-1,y:-1,cost:i*y(s.x-1,s.y-1)}),s.x0&&(c||d(t,e,s.x,s.y-1)&&d(t,e,s.x+1,s.y))&&a.push({instance:l[0],searchNode:s,x:1,y:-1,cost:i*y(s.x+1,s.y-1)}),s.x>0&&s.ye?-1:e===o?0:1});for(var u=!1,v=0;vr?i*r+s:i*s+r}var r=Math.abs(t-e),s=Math.abs(n-o);return r+s}}; \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 9b77f11..fb5b750 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,7 +1,7 @@ var gulp = require('gulp'); var concat = require('gulp-concat'); var config = require('./package.json'); -var sources = ['src/package-managers.js', 'src/node.js', 'src/priority-queue.js', 'src/instance.js', 'src/easystar.js']; +var sources = ['node_modules/heap/lib/heap.js', 'src/package-managers.js', 'src/node.js', 'src/priority-queue.js', 'src/instance.js', 'src/easystar.js']; var uglify = require('gulp-uglify'); var karma = require('gulp-karma'); diff --git a/package.json b/package.json index 3620903..339d531 100644 --- a/package.json +++ b/package.json @@ -30,5 +30,7 @@ "karma-jasmine": "^0.3.5", "karma-phantomjs-launcher": "^0.1.4" }, - "dependencies": {} + "dependencies": { + "heap": "^0.2.6" + } } diff --git a/src/easystar.js b/src/easystar.js index 80563e3..0fc85dd 100755 --- a/src/easystar.js +++ b/src/easystar.js @@ -240,7 +240,9 @@ EasyStar.js = function() { // Create the instance var instance = new EasyStar.instance(); - instance.openList = new EasyStar.PriorityQueue("bestGuessDistance",EasyStar.PriorityQueue.MIN_HEAP); + instance.openList = new Heap(function(nodeA, nodeB) { + return nodeA.bestGuessDistance() - nodeB.bestGuessDistance(); + }) instance.isDoneCalculating = false; instance.nodeHash = {}; instance.startX = startX; @@ -249,7 +251,7 @@ EasyStar.js = function() { instance.endY = endY; instance.callback = callbackWrapper; - instance.openList.insert(coordinateToNode(instance, instance.startX, + instance.openList.push(coordinateToNode(instance, instance.startX, instance.startY, null, STRAIGHT_COST)); instances.push(instance); @@ -276,14 +278,14 @@ EasyStar.js = function() { } // Couldn't find a path. - if (instances[0].openList.length === 0) { + if (instances[0].openList.size() === 0) { var ic = instances[0]; ic.callback(null); instances.shift(); continue; } - var searchNode = instances[0].openList.shiftHighestPriorityElement(); + var searchNode = instances[0].openList.pop(); var tilesToSearch = []; searchNode.list = EasyStar.Node.CLOSED_LIST; @@ -416,12 +418,10 @@ EasyStar.js = function() { if (node.list === undefined) { node.list = EasyStar.Node.OPEN_LIST; - instance.openList.insert(node); - } else if (node.list === EasyStar.Node.OPEN_LIST) { - if (searchNode.costSoFar + cost < node.costSoFar) { - node.costSoFar = searchNode.costSoFar + cost; - node.parent = searchNode; - } + instance.openList.push(node); + } else if (searchNode.costSoFar + cost < node.costSoFar) { + node.costSoFar = searchNode.costSoFar + cost; + instance.openList.updateItem(node); } } } diff --git a/src/priority-queue.js b/src/priority-queue.js deleted file mode 100755 index 285af90..0000000 --- a/src/priority-queue.js +++ /dev/null @@ -1,153 +0,0 @@ -/** -* This is an improved Priority Queue data type implementation that can be used to sort any object type. -* It uses a technique called a binary heap. -* -* For more on binary heaps see: http://en.wikipedia.org/wiki/Binary_heap -* -* @param {String} criteria The criteria by which to sort the objects. -* This should be a property of the objects you're sorting. -* -* @param {Number} heapType either PriorityQueue.MAX_HEAP or PriorityQueue.MIN_HEAP. -**/ -EasyStar.PriorityQueue = function(criteria,heapType) { - this.length = 0; //The current length of heap. - var queue = []; - var isMax = false; - - //Constructor - if (heapType==EasyStar.PriorityQueue.MAX_HEAP) { - isMax = true; - } else if (heapType==EasyStar.PriorityQueue.MIN_HEAP) { - isMax = false; - } else { - throw heapType + " not supported."; - } - - /** - * Inserts the value into the heap and sorts it. - * - * @param value The object to insert into the heap. - **/ - this.insert = function(value) { - if (!value.hasOwnProperty(criteria)) { - throw "Cannot insert " + value + " because it does not have a property by the name of " + criteria + "."; - } - queue.push(value); - this.length++; - bubbleUp(this.length-1); - } - - /** - * Peeks at the highest priority element. - * - * @return the highest priority element - **/ - this.getHighestPriorityElement = function() { - return queue[0]; - } - - /** - * Removes and returns the highest priority element from the queue. - * - * @return the highest priority element - **/ - this.shiftHighestPriorityElement = function() { - if (this.length === 0) { - throw ("There are no more elements in your priority queue."); - } else if (this.length === 1) { - var onlyValue = queue[0]; - queue = []; - this.length = 0; - return onlyValue; - } - var oldRoot = queue[0]; - var newRoot = queue.pop(); - this.length--; - queue[0] = newRoot; - swapUntilQueueIsCorrect(0); - return oldRoot; - } - - var bubbleUp = function(index) { - if (index===0) { - return; - } - var parent = getParentOf(index); - if (evaluate(index,parent)) { - swap(index,parent); - bubbleUp(parent); - } else { - return; - } - } - - var swapUntilQueueIsCorrect = function(value) { - var left = getLeftOf(value); - var right = getRightOf(value); - if (evaluate(left,value)) { - swap(value,left); - swapUntilQueueIsCorrect(left); - } else if (evaluate(right,value)) { - swap(value,right); - swapUntilQueueIsCorrect(right); - } else if (value==0) { - return; - } else { - swapUntilQueueIsCorrect(0); - } - } - - var swap = function(self,target) { - var placeHolder = queue[self]; - queue[self] = queue[target]; - queue[target] = placeHolder; - } - - var evaluate = function(self,target) { - if (queue[target]===undefined||queue[self]===undefined) { - return false; - } - - var selfValue; - var targetValue; - - // Check if the criteria should be the result of a function call. - if (typeof queue[self][criteria] === 'function') { - selfValue = queue[self][criteria](); - targetValue = queue[target][criteria](); - } else { - selfValue = queue[self][criteria]; - targetValue = queue[target][criteria]; - } - - if (isMax) { - if (selfValue > targetValue) { - return true; - } else { - return false; - } - } else { - if (selfValue < targetValue) { - return true; - } else { - return false; - } - } - } - - var getParentOf = function(index) { - return Math.floor((index-1) / 2); - } - - var getLeftOf = function(index) { - return index*2 + 1; - } - - var getRightOf = function(index) { - return index*2 + 2; - } -}; - -// Constants -EasyStar.PriorityQueue.MAX_HEAP = 0; -EasyStar.PriorityQueue.MIN_HEAP = 1; diff --git a/test/priorityqueuetest.js b/test/priorityqueuetest.js deleted file mode 100644 index e1a5778..0000000 --- a/test/priorityqueuetest.js +++ /dev/null @@ -1,73 +0,0 @@ -describe("PriorityQueue", function() { - - beforeEach(function() { }); - - it("It should sort objects by min value.", function() { - p = new EasyStar.PriorityQueue("value", EasyStar.PriorityQueue.MIN_HEAP); - - var object1 = {}; - var object2 = {}; - var object3 = {}; - var object4 = {}; - - object1.value = 5; - object2.value = 10; - object3.value = 20; - object4.value = 15; - - p.insert(object1); - p.insert(object2); - p.insert(object3); - p.insert(object4); - - expect(p.shiftHighestPriorityElement()).toBe(object1); - expect(p.shiftHighestPriorityElement()).toBe(object2); - expect(p.shiftHighestPriorityElement()).toBe(object4); - expect(p.shiftHighestPriorityElement()).toBe(object3); - expect(p.length).toBe(0); - }); - - it("It should sort two objects by min value", function() { - p = new EasyStar.PriorityQueue("value", EasyStar.PriorityQueue.MIN_HEAP); - - var object1 = {}; - var object2 = {}; - - object1.value = 10; - object2.value = 5; - - p.insert(object1); - expect(p.getHighestPriorityElement()).toBe(object1); - - p.insert(object2); - expect(p.getHighestPriorityElement()).toBe(object2); - - expect(p.shiftHighestPriorityElement()).toBe(object2); - }); - - it("It should sort objects by max value.", function() { - p = new EasyStar.PriorityQueue("value", EasyStar.PriorityQueue.MAX_HEAP); - - var object1 = {}; - var object2 = {}; - var object3 = {}; - var object4 = {}; - - object1.value = 5; - object2.value = 10; - object3.value = 20; - object4.value = 15; - - p.insert(object1); - p.insert(object2); - p.insert(object3); - p.insert(object4); - - expect(p.shiftHighestPriorityElement()).toBe(object3); - expect(p.shiftHighestPriorityElement()).toBe(object4); - expect(p.shiftHighestPriorityElement()).toBe(object2); - expect(p.shiftHighestPriorityElement()).toBe(object1); - expect(p.length).toBe(0); - }); - -}); \ No newline at end of file From 12feff6ac332a6574c806b5339f7eeaa2ec708b4 Mon Sep 17 00:00:00 2001 From: Bryce Neal Date: Mon, 16 May 2016 17:48:40 -0700 Subject: [PATCH 03/10] Move around base case --- src/easystar.js | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/easystar.js b/src/easystar.js index 0fc85dd..5fc90f7 100755 --- a/src/easystar.js +++ b/src/easystar.js @@ -287,6 +287,23 @@ EasyStar.js = function() { var searchNode = instances[0].openList.pop(); + // Handles the case where we have found the destination + if (instances[0].endX === searchNode.x && instances[0].endY === searchNode.y) { + instances[0].isDoneCalculating = true; + var path = []; + path.push(x: searchNode.x, y: searchNode.y); + var parent = searchNode.parent; + while (parent!=null) { + path.push({x: parent.x, y:parent.y}); + parent = parent.parent; + } + path.reverse(); + var ic = instances[0]; + var ip = path; + ic.callback(ip); + return + } + var tilesToSearch = []; searchNode.list = EasyStar.Node.CLOSED_LIST; @@ -389,30 +406,8 @@ EasyStar.js = function() { var adjacentCoordinateX = searchNode.x+x; var adjacentCoordinateY = searchNode.y+y; - if (pointsToAvoid[adjacentCoordinateX + "_" + adjacentCoordinateY] === undefined) { - // Handles the case where we have found the destination - if (instance.endX === adjacentCoordinateX && instance.endY === adjacentCoordinateY) { - instance.isDoneCalculating = true; - var path = []; - var pathLen = 0; - path[pathLen] = {x: adjacentCoordinateX, y: adjacentCoordinateY}; - pathLen++; - path[pathLen] = {x: searchNode.x, y:searchNode.y}; - pathLen++; - var parent = searchNode.parent; - while (parent!=null) { - path[pathLen] = {x: parent.x, y:parent.y}; - pathLen++; - parent = parent.parent; - } - path.reverse(); - var ic = instance; - var ip = path; - ic.callback(ip); - return - } - - if (isTileWalkable(collisionGrid, acceptableTiles, adjacentCoordinateX, adjacentCoordinateY)) { + if (pointsToAvoid[adjacentCoordinateX + "_" + adjacentCoordinateY] === undefined && + isTileWalkable(collisionGrid, acceptableTiles, adjacentCoordinateX, adjacentCoordinateY)) { var node = coordinateToNode(instance, adjacentCoordinateX, adjacentCoordinateY, searchNode, cost); From d3aaf75e962dd4ecf01f55f754df50a77f9d983e Mon Sep 17 00:00:00 2001 From: Bryce Neal Date: Mon, 16 May 2016 17:48:51 -0700 Subject: [PATCH 04/10] Fix starting cost --- src/easystar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/easystar.js b/src/easystar.js index 5fc90f7..dd4703c 100755 --- a/src/easystar.js +++ b/src/easystar.js @@ -445,7 +445,7 @@ EasyStar.js = function() { if (parent!==null) { var costSoFar = parent.costSoFar + cost; } else { - costSoFar = simpleDistanceToTarget; + costSoFar = 0; } var node = new EasyStar.Node(parent,x,y,costSoFar,simpleDistanceToTarget); instance.nodeHash[x + "_" + y] = node; From 1fa35d76df04e078b85a88c4fba0cbdc124d87b7 Mon Sep 17 00:00:00 2001 From: Bryce Neal Date: Mon, 16 May 2016 17:49:34 -0700 Subject: [PATCH 05/10] Remove unnecessary sort step --- src/easystar.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/easystar.js b/src/easystar.js index dd4703c..a102678 100755 --- a/src/easystar.js +++ b/src/easystar.js @@ -368,19 +368,6 @@ EasyStar.js = function() { } } - // First sort all of the potential nodes we could search by their cost + heuristic distance. - tilesToSearch.sort(function(a, b) { - var aCost = a.cost + getDistance(searchNode.x + a.x, searchNode.y + a.y, instances[0].endX, instances[0].endY) - var bCost = b.cost + getDistance(searchNode.x + b.x, searchNode.y + b.y, instances[0].endX, instances[0].endY) - if (aCost < bCost) { - return -1; - } else if (aCost === bCost) { - return 0; - } else { - return 1; - } - }); - var isDoneCalculating = false; // Search all of the surrounding nodes From 8933d819d1c10672b843e0bbaaa365353e42267d Mon Sep 17 00:00:00 2001 From: Bryce Neal Date: Mon, 16 May 2016 17:52:45 -0700 Subject: [PATCH 06/10] Set the parent --- src/easystar.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/easystar.js b/src/easystar.js index a102678..03868cf 100755 --- a/src/easystar.js +++ b/src/easystar.js @@ -403,6 +403,7 @@ EasyStar.js = function() { instance.openList.push(node); } else if (searchNode.costSoFar + cost < node.costSoFar) { node.costSoFar = searchNode.costSoFar + cost; + node.parent = searchNode; instance.openList.updateItem(node); } } From 08470dee5a647571da214812eb6ce96fc53c62e5 Mon Sep 17 00:00:00 2001 From: Bryce Neal Date: Mon, 16 May 2016 19:48:16 -0700 Subject: [PATCH 07/10] Switch to webpack from gulp --- README.md | 2 +- bin/easystar-0.2.3.js | 1764 +++++++++++++++++++------------------ bin/easystar-0.2.3.min.js | 2 +- gulpfile.js | 36 - karma.conf.js | 5 +- package.json | 75 +- src/easystar.js | 920 +++++++++---------- src/instance.js | 20 +- src/node.js | 30 +- src/package-managers.js | 14 - test/easystartest.js | 390 ++++---- webpack.config.js | 35 + 12 files changed, 1655 insertions(+), 1638 deletions(-) mode change 100755 => 100644 bin/easystar-0.2.3.js mode change 100755 => 100644 bin/easystar-0.2.3.min.js delete mode 100644 gulpfile.js delete mode 100644 src/package-managers.js create mode 100644 webpack.config.js diff --git a/README.md b/README.md index a5c89bf..8397de4 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ Open your browser to 127.0.0.1:3000 to see the example. ## Testing -`gulp test` +`npm run test` ## Support diff --git a/bin/easystar-0.2.3.js b/bin/easystar-0.2.3.js old mode 100755 new mode 100644 index 2deaf30..1e0f601 --- a/bin/easystar-0.2.3.js +++ b/bin/easystar-0.2.3.js @@ -1,908 +1,926 @@ -// 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); - }; +var EasyStar = +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; - Heap.prototype.insert = Heap.prototype.push; +/******/ // The require function +/******/ function __webpack_require__(moduleId) { - Heap.prototype.top = Heap.prototype.peek; +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; - Heap.prototype.front = Heap.prototype.peek; +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; - Heap.prototype.has = Heap.prototype.contains; +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - Heap.prototype.copy = Heap.prototype.clone; +/******/ // Flag the module as loaded +/******/ module.loaded = true; - return Heap; +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } - })(); - (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; - }); +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; -}).call(this); +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; -// NameSpace -var EasyStar = EasyStar || {}; +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; -// For require.js -if (typeof define === "function" && define.amd) { - define("easystar", [], function() { - return EasyStar; - }); -} - -// For browserify and node.js -if (typeof module !== 'undefined' && module.exports) { - module.exports = EasyStar; -} -/** -* A simple Node that represents a single tile on the grid. -* @param {Object} parent The parent node. -* @param {Number} x The x position on the grid. -* @param {Number} y The y position on the grid. -* @param {Number} costSoFar How far this node is in moves*cost from the start. -* @param {Number} simpleDistanceToTarget Manhatten distance to the end point. -**/ -EasyStar.Node = function(parent, x, y, costSoFar, simpleDistanceToTarget) { - this.parent = parent; - this.x = x; - this.y = y; - this.costSoFar = costSoFar; - this.simpleDistanceToTarget = simpleDistanceToTarget; - - /** - * @return {Number} Best guess distance of a cost using this node. - **/ - this.bestGuessDistance = function() { - return this.costSoFar + this.simpleDistanceToTarget; - } -}; - -// Constants -EasyStar.Node.OPEN_LIST = 0; -EasyStar.Node.CLOSED_LIST = 1; -/** - * Represents a single instance of EasyStar. - * A path that is in the queue to eventually be found. - */ -EasyStar.instance = function() { - this.isDoneCalculating = true; - this.pointsToAvoid = {}; - this.startX; - this.callback; - this.startY; - this.endX; - this.endY; - this.nodeHash = {}; - this.openList; -}; -/** -* EasyStar.js -* github.com/prettymuchbryce/EasyStarJS -* Licensed under the MIT license. -* -* Implementation By Bryce Neal (@prettymuchbryce) -**/ -EasyStar.js = function() { - var STRAIGHT_COST = 1.0; - var DIAGONAL_COST = 1.4; - var syncEnabled = false; - var pointsToAvoid = {}; - var collisionGrid; - var costMap = {}; - var pointsToCost = {}; - var allowCornerCutting = true; - var iterationsSoFar; - var instances = []; - var iterationsPerCalculation = Number.MAX_VALUE; - var acceptableTiles; - var diagonalsEnabled = false; +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { /** - * Sets the collision grid that EasyStar uses. + * EasyStar.js + * github.com/prettymuchbryce/EasyStarJS + * Licensed under the MIT license. * - * @param {Array|Number} tiles An array of numbers that represent - * which tiles in your grid should be considered - * acceptable, or "walkable". - **/ - this.setAcceptableTiles = function(tiles) { - if (tiles instanceof Array) { - // Array - acceptableTiles = tiles; - } else if (!isNaN(parseFloat(tiles)) && isFinite(tiles)) { - // Number - acceptableTiles = [tiles]; - } - }; - - /** - * Enables sync mode for this EasyStar instance.. - * if you're into that sort of thing. - **/ - this.enableSync = function() { - syncEnabled = true; - }; - - /** - * Disables sync mode for this EasyStar instance. - **/ - this.disableSync = function() { - syncEnabled = false; - }; - - /** - * Enable diagonal pathfinding. - */ - this.enableDiagonals = function() { - diagonalsEnabled = true; - } - - /** - * Disable diagonal pathfinding. - */ - this.disableDiagonals = function() { - diagonalsEnabled = false; - } - - /** - * Sets the collision grid that EasyStar uses. - * - * @param {Array} grid The collision grid that this EasyStar instance will read from. - * This should be a 2D Array of Numbers. - **/ - this.setGrid = function(grid) { - collisionGrid = grid; - - //Setup cost map - for (var y = 0; y < collisionGrid.length; y++) { - for (var x = 0; x < collisionGrid[0].length; x++) { - if (!costMap[collisionGrid[y][x]]) { - costMap[collisionGrid[y][x]] = 1 - } - } - } - }; - - /** - * Sets the tile cost for a particular tile type. - * - * @param {Number} The tile type to set the cost for. - * @param {Number} The multiplicative cost associated with the given tile. - **/ - this.setTileCost = function(tileType, cost) { - costMap[tileType] = cost; - }; - - /** - * Sets the an additional cost for a particular point. - * Overrides the cost from setTileCost. - * - * @param {Number} x The x value of the point to cost. - * @param {Number} y The y value of the point to cost. - * @param {Number} The multiplicative cost associated with the given point. - **/ - this.setAdditionalPointCost = function(x, y, cost) { - pointsToCost[x + '_' + y] = cost; - }; - - /** - * Remove the additional cost for a particular point. - * - * @param {Number} x The x value of the point to stop costing. - * @param {Number} y The y value of the point to stop costing. + * Implementation By Bryce Neal (@prettymuchbryce) **/ - this.removeAdditionalPointCost = function(x, y) { - delete pointsToCost[x + '_' + y]; - } - /** - * Remove all additional point costs. - **/ - this.removeAllAdditionalPointCosts = function() { - pointsToCost = {}; - } + var EasyStar = {}; + var Instance = __webpack_require__(1); + var Node = __webpack_require__(2); + var Heap = __webpack_require__(3); - /** - * Sets the number of search iterations per calculation. - * A lower number provides a slower result, but more practical if you - * have a large tile-map and don't want to block your thread while - * finding a path. - * - * @param {Number} iterations The number of searches to prefrom per calculate() call. - **/ - this.setIterationsPerCalculation = function(iterations) { - iterationsPerCalculation = iterations; - }; - - /** - * Avoid a particular point on the grid, - * regardless of whether or not it is an acceptable tile. - * - * @param {Number} x The x value of the point to avoid. - * @param {Number} y The y value of the point to avoid. - **/ - this.avoidAdditionalPoint = function(x, y) { - pointsToAvoid[x + "_" + y] = 1; - }; + const CLOSED_LIST = 0; + const OPEN_LIST = 1; - /** - * Stop avoiding a particular point on the grid. - * - * @param {Number} x The x value of the point to stop avoiding. - * @param {Number} y The y value of the point to stop avoiding. - **/ - this.stopAvoidingAdditionalPoint = function(x, y) { - delete pointsToAvoid[x + "_" + y]; - }; + module.exports = EasyStar; - /** - * Enables corner cutting in diagonal movement. - **/ - this.enableCornerCutting = function() { - allowCornerCutting = true; + EasyStar.js = function () { + var STRAIGHT_COST = 1.0; + var DIAGONAL_COST = 1.4; + var syncEnabled = false; + var pointsToAvoid = {}; + var collisionGrid; + var costMap = {}; + var pointsToCost = {}; + var allowCornerCutting = true; + var iterationsSoFar; + var instances = []; + var iterationsPerCalculation = Number.MAX_VALUE; + var acceptableTiles; + var diagonalsEnabled = false; + + /** + * Sets the collision grid that EasyStar uses. + * + * @param {Array|Number} tiles An array of numbers that represent + * which tiles in your grid should be considered + * acceptable, or "walkable". + **/ + this.setAcceptableTiles = function (tiles) { + if (tiles instanceof Array) { + // Array + acceptableTiles = tiles; + } else if (!isNaN(parseFloat(tiles)) && isFinite(tiles)) { + // Number + acceptableTiles = [tiles]; + } + }; + + /** + * Enables sync mode for this EasyStar instance.. + * if you're into that sort of thing. + **/ + this.enableSync = function () { + syncEnabled = true; + }; + + /** + * Disables sync mode for this EasyStar instance. + **/ + this.disableSync = function () { + syncEnabled = false; + }; + + /** + * Enable diagonal pathfinding. + */ + this.enableDiagonals = function () { + diagonalsEnabled = true; + }; + + /** + * Disable diagonal pathfinding. + */ + this.disableDiagonals = function () { + diagonalsEnabled = false; + }; + + /** + * Sets the collision grid that EasyStar uses. + * + * @param {Array} grid The collision grid that this EasyStar instance will read from. + * This should be a 2D Array of Numbers. + **/ + this.setGrid = function (grid) { + collisionGrid = grid; + + //Setup cost map + for (var y = 0; y < collisionGrid.length; y++) { + for (var x = 0; x < collisionGrid[0].length; x++) { + if (!costMap[collisionGrid[y][x]]) { + costMap[collisionGrid[y][x]] = 1; + } + } + } + }; + + /** + * Sets the tile cost for a particular tile type. + * + * @param {Number} The tile type to set the cost for. + * @param {Number} The multiplicative cost associated with the given tile. + **/ + this.setTileCost = function (tileType, cost) { + costMap[tileType] = cost; + }; + + /** + * Sets the an additional cost for a particular point. + * Overrides the cost from setTileCost. + * + * @param {Number} x The x value of the point to cost. + * @param {Number} y The y value of the point to cost. + * @param {Number} The multiplicative cost associated with the given point. + **/ + this.setAdditionalPointCost = function (x, y, cost) { + pointsToCost[x + '_' + y] = cost; + }; + + /** + * Remove the additional cost for a particular point. + * + * @param {Number} x The x value of the point to stop costing. + * @param {Number} y The y value of the point to stop costing. + **/ + this.removeAdditionalPointCost = function (x, y) { + delete pointsToCost[x + '_' + y]; + }; + + /** + * Remove all additional point costs. + **/ + this.removeAllAdditionalPointCosts = function () { + pointsToCost = {}; + }; + + /** + * Sets the number of search iterations per calculation. + * A lower number provides a slower result, but more practical if you + * have a large tile-map and don't want to block your thread while + * finding a path. + * + * @param {Number} iterations The number of searches to prefrom per calculate() call. + **/ + this.setIterationsPerCalculation = function (iterations) { + iterationsPerCalculation = iterations; + }; + + /** + * Avoid a particular point on the grid, + * regardless of whether or not it is an acceptable tile. + * + * @param {Number} x The x value of the point to avoid. + * @param {Number} y The y value of the point to avoid. + **/ + this.avoidAdditionalPoint = function (x, y) { + pointsToAvoid[x + "_" + y] = 1; + }; + + /** + * Stop avoiding a particular point on the grid. + * + * @param {Number} x The x value of the point to stop avoiding. + * @param {Number} y The y value of the point to stop avoiding. + **/ + this.stopAvoidingAdditionalPoint = function (x, y) { + delete pointsToAvoid[x + "_" + y]; + }; + + /** + * Enables corner cutting in diagonal movement. + **/ + this.enableCornerCutting = function () { + allowCornerCutting = true; + }; + + /** + * Disables corner cutting in diagonal movement. + **/ + this.disableCornerCutting = function () { + allowCornerCutting = false; + }; + + /** + * Stop avoiding all additional points on the grid. + **/ + this.stopAvoidingAllAdditionalPoints = function () { + pointsToAvoid = {}; + }; + + /** + * Find a path. + * + * @param {Number} startX The X position of the starting point. + * @param {Number} startY The Y position of the starting point. + * @param {Number} endX The X position of the ending point. + * @param {Number} endY The Y position of the ending point. + * @param {Function} callback A function that is called when your path + * is found, or no path is found. + * + **/ + this.findPath = function (startX, startY, endX, endY, callback) { + // Wraps the callback for sync vs async logic + var callbackWrapper = function (result) { + if (syncEnabled) { + callback(result); + } else { + setTimeout(function () { + callback(result); + }); + } + }; + + // No acceptable tiles were set + if (acceptableTiles === undefined) { + throw new Error("You can't set a path without first calling setAcceptableTiles() on EasyStar."); + } + // No grid was set + if (collisionGrid === undefined) { + throw new Error("You can't set a path without first calling setGrid() on EasyStar."); + } + + // Start or endpoint outside of scope. + if (startX < 0 || startY < 0 || endX < 0 || endY < 0 || startX > collisionGrid[0].length - 1 || startY > collisionGrid.length - 1 || endX > collisionGrid[0].length - 1 || endY > collisionGrid.length - 1) { + throw new Error("Your start or end point is outside the scope of your grid."); + } + + // Start and end are the same tile. + if (startX === endX && startY === endY) { + callbackWrapper([]); + return; + } + + // End point is not an acceptable tile. + var endTile = collisionGrid[endY][endX]; + var isAcceptable = false; + for (var i = 0; i < acceptableTiles.length; i++) { + if (endTile === acceptableTiles[i]) { + isAcceptable = true; + break; + } + } + + if (isAcceptable === false) { + callbackWrapper(null); + return; + } + + // Create the instance + var instance = new Instance(); + instance.openList = new Heap(function (nodeA, nodeB) { + return nodeA.bestGuessDistance() - nodeB.bestGuessDistance(); + }); + instance.isDoneCalculating = false; + instance.nodeHash = {}; + instance.startX = startX; + instance.startY = startY; + instance.endX = endX; + instance.endY = endY; + instance.callback = callbackWrapper; + + instance.openList.push(coordinateToNode(instance, instance.startX, instance.startY, null, STRAIGHT_COST)); + + instances.push(instance); + }; + + /** + * This method steps through the A* Algorithm in an attempt to + * find your path(s). It will search 4-8 tiles (depending on diagonals) for every calculation. + * You can change the number of calculations done in a call by using + * easystar.setIteratonsPerCalculation(). + **/ + this.calculate = function () { + if (instances.length === 0 || collisionGrid === undefined || acceptableTiles === undefined) { + return; + } + for (iterationsSoFar = 0; iterationsSoFar < iterationsPerCalculation; iterationsSoFar++) { + if (instances.length === 0) { + return; + } + + if (syncEnabled) { + // If this is a sync instance, we want to make sure that it calculates synchronously. + iterationsSoFar = 0; + } + + // Couldn't find a path. + if (instances[0].openList.size() === 0) { + var ic = instances[0]; + ic.callback(null); + instances.shift(); + continue; + } + + var searchNode = instances[0].openList.pop(); + + // Handles the case where we have found the destination + if (instances[0].endX === searchNode.x && instances[0].endY === searchNode.y) { + instances[0].isDoneCalculating = true; + var path = []; + path.push({ x: searchNode.x, y: searchNode.y }); + var parent = searchNode.parent; + while (parent != null) { + path.push({ x: parent.x, y: parent.y }); + parent = parent.parent; + } + path.reverse(); + var ic = instances[0]; + var ip = path; + ic.callback(ip); + return; + } + + var tilesToSearch = []; + searchNode.list = CLOSED_LIST; + + if (searchNode.y > 0) { + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: 0, y: -1, cost: STRAIGHT_COST * getTileCost(searchNode.x, searchNode.y - 1) }); + } + if (searchNode.x < collisionGrid[0].length - 1) { + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: 1, y: 0, cost: STRAIGHT_COST * getTileCost(searchNode.x + 1, searchNode.y) }); + } + if (searchNode.y < collisionGrid.length - 1) { + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: 0, y: 1, cost: STRAIGHT_COST * getTileCost(searchNode.x, searchNode.y + 1) }); + } + if (searchNode.x > 0) { + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: -1, y: 0, cost: STRAIGHT_COST * getTileCost(searchNode.x - 1, searchNode.y) }); + } + if (diagonalsEnabled) { + if (searchNode.x > 0 && searchNode.y > 0) { + + if (allowCornerCutting || isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y - 1) && isTileWalkable(collisionGrid, acceptableTiles, searchNode.x - 1, searchNode.y)) { + + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: -1, y: -1, cost: DIAGONAL_COST * getTileCost(searchNode.x - 1, searchNode.y - 1) }); + } + } + if (searchNode.x < collisionGrid[0].length - 1 && searchNode.y < collisionGrid.length - 1) { + + if (allowCornerCutting || isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y + 1) && isTileWalkable(collisionGrid, acceptableTiles, searchNode.x + 1, searchNode.y)) { + + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: 1, y: 1, cost: DIAGONAL_COST * getTileCost(searchNode.x + 1, searchNode.y + 1) }); + } + } + if (searchNode.x < collisionGrid[0].length - 1 && searchNode.y > 0) { + + if (allowCornerCutting || isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y - 1) && isTileWalkable(collisionGrid, acceptableTiles, searchNode.x + 1, searchNode.y)) { + + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: 1, y: -1, cost: DIAGONAL_COST * getTileCost(searchNode.x + 1, searchNode.y - 1) }); + } + } + if (searchNode.x > 0 && searchNode.y < collisionGrid.length - 1) { + + if (allowCornerCutting || isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y + 1) && isTileWalkable(collisionGrid, acceptableTiles, searchNode.x - 1, searchNode.y)) { + + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: -1, y: 1, cost: DIAGONAL_COST * getTileCost(searchNode.x - 1, searchNode.y + 1) }); + } + } + } + + var isDoneCalculating = false; + + // Search all of the surrounding nodes + for (var i = 0; i < tilesToSearch.length; i++) { + checkAdjacentNode(tilesToSearch[i].instance, tilesToSearch[i].searchNode, tilesToSearch[i].x, tilesToSearch[i].y, tilesToSearch[i].cost); + if (tilesToSearch[i].instance.isDoneCalculating === true) { + isDoneCalculating = true; + break; + } + } + + if (isDoneCalculating) { + instances.shift(); + continue; + } + } + }; + + // Private methods follow + var checkAdjacentNode = function (instance, searchNode, x, y, cost) { + var adjacentCoordinateX = searchNode.x + x; + var adjacentCoordinateY = searchNode.y + y; + + if (pointsToAvoid[adjacentCoordinateX + "_" + adjacentCoordinateY] === undefined && isTileWalkable(collisionGrid, acceptableTiles, adjacentCoordinateX, adjacentCoordinateY)) { + var node = coordinateToNode(instance, adjacentCoordinateX, adjacentCoordinateY, searchNode, cost); + + if (node.list === undefined) { + node.list = OPEN_LIST; + instance.openList.push(node); + } else if (searchNode.costSoFar + cost < node.costSoFar) { + node.costSoFar = searchNode.costSoFar + cost; + node.parent = searchNode; + instance.openList.updateItem(node); + } + } + }; + + // Helpers + var isTileWalkable = function (collisionGrid, acceptableTiles, x, y) { + for (var i = 0; i < acceptableTiles.length; i++) { + if (collisionGrid[y][x] === acceptableTiles[i]) { + return true; + } + } + + return false; + }; + + var getTileCost = function (x, y) { + return pointsToCost[x + '_' + y] || costMap[collisionGrid[y][x]]; + }; + + var coordinateToNode = function (instance, x, y, parent, cost) { + if (instance.nodeHash[x + "_" + y] !== undefined) { + return instance.nodeHash[x + "_" + y]; + } + var simpleDistanceToTarget = getDistance(x, y, instance.endX, instance.endY); + if (parent !== null) { + var costSoFar = parent.costSoFar + cost; + } else { + costSoFar = 0; + } + var node = new Node(parent, x, y, costSoFar, simpleDistanceToTarget); + instance.nodeHash[x + "_" + y] = node; + return node; + }; + + var getDistance = function (x1, y1, x2, y2) { + if (diagonalsEnabled) { + // Octile distance + var dx = Math.abs(x1 - x2); + var dy = Math.abs(y1 - y2); + if (dx < dy) { + return DIAGONAL_COST * dx + dy; + } else { + return DIAGONAL_COST * dy + dx; + } + } else { + // Manhattan distance + var dx = Math.abs(x1 - x2); + var dy = Math.abs(y1 - y2); + return dx + dy; + } + }; }; - /** - * Disables corner cutting in diagonal movement. - **/ - this.disableCornerCutting = function() { - allowCornerCutting = false; - }; +/***/ }, +/* 1 */ +/***/ function(module, exports) { /** - * Stop avoiding all additional points on the grid. - **/ - this.stopAvoidingAllAdditionalPoints = function() { - pointsToAvoid = {}; + * Represents a single instance of EasyStar. + * A path that is in the queue to eventually be found. + */ + module.exports = function () { + this.isDoneCalculating = true; + this.pointsToAvoid = {}; + this.startX; + this.callback; + this.startY; + this.endX; + this.endY; + this.nodeHash = {}; + this.openList; }; - /** - * Find a path. - * - * @param {Number} startX The X position of the starting point. - * @param {Number} startY The Y position of the starting point. - * @param {Number} endX The X position of the ending point. - * @param {Number} endY The Y position of the ending point. - * @param {Function} callback A function that is called when your path - * is found, or no path is found. - * - **/ - this.findPath = function(startX, startY, endX, endY, callback) { - // Wraps the callback for sync vs async logic - var callbackWrapper = function(result) { - if (syncEnabled) { - callback(result); - } else { - setTimeout(function() { - callback(result); - }); - } - } - - // No acceptable tiles were set - if (acceptableTiles === undefined) { - throw new Error("You can't set a path without first calling setAcceptableTiles() on EasyStar."); - } - // No grid was set - if (collisionGrid === undefined) { - throw new Error("You can't set a path without first calling setGrid() on EasyStar."); - } - - // Start or endpoint outside of scope. - if (startX < 0 || startY < 0 || endX < 0 || endY < 0 || - startX > collisionGrid[0].length-1 || startY > collisionGrid.length-1 || - endX > collisionGrid[0].length-1 || endY > collisionGrid.length-1) { - throw new Error("Your start or end point is outside the scope of your grid."); - } - - // Start and end are the same tile. - if (startX===endX && startY===endY) { - callbackWrapper([]); - return; - } - - // End point is not an acceptable tile. - var endTile = collisionGrid[endY][endX]; - var isAcceptable = false; - for (var i = 0; i < acceptableTiles.length; i++) { - if (endTile === acceptableTiles[i]) { - isAcceptable = true; - break; - } - } - - if (isAcceptable === false) { - callbackWrapper(null); - return; - } - - // Create the instance - var instance = new EasyStar.instance(); - instance.openList = new Heap(function(nodeA, nodeB) { - return nodeA.bestGuessDistance() - nodeB.bestGuessDistance(); - }) - instance.isDoneCalculating = false; - instance.nodeHash = {}; - instance.startX = startX; - instance.startY = startY; - instance.endX = endX; - instance.endY = endY; - instance.callback = callbackWrapper; - - instance.openList.push(coordinateToNode(instance, instance.startX, - instance.startY, null, STRAIGHT_COST)); - - instances.push(instance); - }; +/***/ }, +/* 2 */ +/***/ function(module, exports) { /** - * This method steps through the A* Algorithm in an attempt to - * find your path(s). It will search 4-8 tiles (depending on diagonals) for every calculation. - * You can change the number of calculations done in a call by using - * easystar.setIteratonsPerCalculation(). + * A simple Node that represents a single tile on the grid. + * @param {Object} parent The parent node. + * @param {Number} x The x position on the grid. + * @param {Number} y The y position on the grid. + * @param {Number} costSoFar How far this node is in moves*cost from the start. + * @param {Number} simpleDistanceToTarget Manhatten distance to the end point. **/ - this.calculate = function() { - if (instances.length === 0 || collisionGrid === undefined || acceptableTiles === undefined) { - return; - } - for (iterationsSoFar = 0; iterationsSoFar < iterationsPerCalculation; iterationsSoFar++) { - if (instances.length === 0) { - return; - } - - if (syncEnabled) { - // If this is a sync instance, we want to make sure that it calculates synchronously. - iterationsSoFar = 0; - } - - // Couldn't find a path. - if (instances[0].openList.size() === 0) { - var ic = instances[0]; - ic.callback(null); - instances.shift(); - continue; - } - - var searchNode = instances[0].openList.pop(); - - var tilesToSearch = []; - searchNode.list = EasyStar.Node.CLOSED_LIST; - - if (searchNode.y > 0) { - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: 0, y: -1, cost: STRAIGHT_COST * getTileCost(searchNode.x, searchNode.y-1)}); - } - if (searchNode.x < collisionGrid[0].length-1) { - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: 1, y: 0, cost: STRAIGHT_COST * getTileCost(searchNode.x+1, searchNode.y)}); - } - if (searchNode.y < collisionGrid.length-1) { - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: 0, y: 1, cost: STRAIGHT_COST * getTileCost(searchNode.x, searchNode.y+1)}); - } - if (searchNode.x > 0) { - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: -1, y: 0, cost: STRAIGHT_COST * getTileCost(searchNode.x-1, searchNode.y)}); - } - if (diagonalsEnabled) { - if (searchNode.x > 0 && searchNode.y > 0) { - - if (allowCornerCutting || - (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y-1) && - isTileWalkable(collisionGrid, acceptableTiles, searchNode.x-1, searchNode.y))) { - - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: -1, y: -1, cost: DIAGONAL_COST * getTileCost(searchNode.x-1, searchNode.y-1)}); - } - } - if (searchNode.x < collisionGrid[0].length-1 && searchNode.y < collisionGrid.length-1) { - - if (allowCornerCutting || - (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y+1) && - isTileWalkable(collisionGrid, acceptableTiles, searchNode.x+1, searchNode.y))) { - - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: 1, y: 1, cost: DIAGONAL_COST * getTileCost(searchNode.x+1, searchNode.y+1)}); - } - } - if (searchNode.x < collisionGrid[0].length-1 && searchNode.y > 0) { - - if (allowCornerCutting || - (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y-1) && - isTileWalkable(collisionGrid, acceptableTiles, searchNode.x+1, searchNode.y))) { - - - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: 1, y: -1, cost: DIAGONAL_COST * getTileCost(searchNode.x+1, searchNode.y-1)}); - } - } - if (searchNode.x > 0 && searchNode.y < collisionGrid.length-1) { - - if (allowCornerCutting || - (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y+1) && - isTileWalkable(collisionGrid, acceptableTiles, searchNode.x-1, searchNode.y))) { - - - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: -1, y: 1, cost: DIAGONAL_COST * getTileCost(searchNode.x-1, searchNode.y+1)}); - } - } - } - - // First sort all of the potential nodes we could search by their cost + heuristic distance. - tilesToSearch.sort(function(a, b) { - var aCost = a.cost + getDistance(searchNode.x + a.x, searchNode.y + a.y, instances[0].endX, instances[0].endY) - var bCost = b.cost + getDistance(searchNode.x + b.x, searchNode.y + b.y, instances[0].endX, instances[0].endY) - if (aCost < bCost) { - return -1; - } else if (aCost === bCost) { - return 0; - } else { - return 1; - } - }); - - var isDoneCalculating = false; - - // Search all of the surrounding nodes - for (var i = 0; i < tilesToSearch.length; i++) { - checkAdjacentNode(tilesToSearch[i].instance, tilesToSearch[i].searchNode, - tilesToSearch[i].x, tilesToSearch[i].y, tilesToSearch[i].cost); - if (tilesToSearch[i].instance.isDoneCalculating === true) { - isDoneCalculating = true; - break; - } - } - - if (isDoneCalculating) { - instances.shift(); - continue; - } - - } + module.exports = function (parent, x, y, costSoFar, simpleDistanceToTarget) { + this.parent = parent; + this.x = x; + this.y = y; + this.costSoFar = costSoFar; + this.simpleDistanceToTarget = simpleDistanceToTarget; + + /** + * @return {Number} Best guess distance of a cost using this node. + **/ + this.bestGuessDistance = function () { + return this.costSoFar + this.simpleDistanceToTarget; + }; }; - // Private methods follow - var checkAdjacentNode = function(instance, searchNode, x, y, cost) { - var adjacentCoordinateX = searchNode.x+x; - var adjacentCoordinateY = searchNode.y+y; - - if (pointsToAvoid[adjacentCoordinateX + "_" + adjacentCoordinateY] === undefined) { - // Handles the case where we have found the destination - if (instance.endX === adjacentCoordinateX && instance.endY === adjacentCoordinateY) { - instance.isDoneCalculating = true; - var path = []; - var pathLen = 0; - path[pathLen] = {x: adjacentCoordinateX, y: adjacentCoordinateY}; - pathLen++; - path[pathLen] = {x: searchNode.x, y:searchNode.y}; - pathLen++; - var parent = searchNode.parent; - while (parent!=null) { - path[pathLen] = {x: parent.x, y:parent.y}; - pathLen++; - parent = parent.parent; - } - path.reverse(); - var ic = instance; - var ip = path; - ic.callback(ip); - return - } - - if (isTileWalkable(collisionGrid, acceptableTiles, adjacentCoordinateX, adjacentCoordinateY)) { - var node = coordinateToNode(instance, adjacentCoordinateX, - adjacentCoordinateY, searchNode, cost); - - if (node.list === undefined) { - node.list = EasyStar.Node.OPEN_LIST; - instance.openList.push(node); - } else if (searchNode.costSoFar + cost < node.costSoFar) { - node.costSoFar = searchNode.costSoFar + cost; - instance.openList.updateItem(node); - } - } - } - }; +/***/ }, +/* 3 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = __webpack_require__(4); + +/***/ }, +/* 4 */ +/***/ function(module, exports, __webpack_require__) { + + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// 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; - // Helpers - var isTileWalkable = function(collisionGrid, acceptableTiles, x, y) { - for (var i = 0; i < acceptableTiles.length; i++) { - if (collisionGrid[y][x] === acceptableTiles[i]) { - return true; - } - } + Heap.prototype.has = Heap.prototype.contains; - return false; - }; + Heap.prototype.copy = Heap.prototype.clone; - var getTileCost = function(x, y) { - return pointsToCost[x + '_' + y] || costMap[collisionGrid[y][x]] - }; + return Heap; + }(); - var coordinateToNode = function(instance, x, y, parent, cost) { - if (instance.nodeHash[x + "_" + y]!==undefined) { - return instance.nodeHash[x + "_" + y]; - } - var simpleDistanceToTarget = getDistance(x, y, instance.endX, instance.endY); - if (parent!==null) { - var costSoFar = parent.costSoFar + cost; - } else { - costSoFar = simpleDistanceToTarget; - } - var node = new EasyStar.Node(parent,x,y,costSoFar,simpleDistanceToTarget); - instance.nodeHash[x + "_" + y] = node; - return node; - }; + (function (root, factory) { + if (true) { + return !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof exports === 'object') { + return module.exports = factory(); + } else { + return root.Heap = factory(); + } + })(this, function () { + return Heap; + }); + }).call(this); - var getDistance = function(x1,y1,x2,y2) { - if (diagonalsEnabled) { - // Octile distance - var dx = Math.abs(x1 - x2); - var dy = Math.abs(y1 - y2); - if (dx < dy) { - return DIAGONAL_COST * dx + dy; - } else { - return DIAGONAL_COST * dy + dx; - } - } else { - // Manhattan distance - var dx = Math.abs(x1 - x2); - var dy = Math.abs(y1 - y2); - return (dx + dy) - } - }; -} \ No newline at end of file +/***/ } +/******/ ]); \ No newline at end of file diff --git a/bin/easystar-0.2.3.min.js b/bin/easystar-0.2.3.min.js old mode 100755 new mode 100644 index 813b449..8251739 --- a/bin/easystar-0.2.3.min.js +++ b/bin/easystar-0.2.3.min.js @@ -1 +1 @@ -(function(){var t,n,e,o,i,r,s,a,u,c,l,h,p,f,d;e=Math.floor,c=Math.min,n=function(t,n){return n>t?-1:t>n?1:0},u=function(t,o,i,r,s){var a;if(null==i&&(i=0),null==s&&(s=n),0>i)throw new Error("lo must be non-negative");for(null==r&&(r=t.length);r>i;)a=e((i+r)/2),s(o,t[a])<0?r=a:i=a+1;return[].splice.apply(t,[i,i-i].concat(o)),o},r=function(t,e,o){return null==o&&(o=n),t.push(e),f(t,0,t.length-1,o)},i=function(t,e){var o,i;return null==e&&(e=n),o=t.pop(),t.length?(i=t[0],t[0]=o,d(t,0,e)):i=o,i},a=function(t,e,o){var i;return null==o&&(o=n),i=t[0],t[0]=e,d(t,0,o),i},s=function(t,e,o){var i;return null==o&&(o=n),t.length&&o(t[0],e)<0&&(i=[t[0],e],e=i[0],t[0]=i[1],d(t,0,o)),e},o=function(t,o){var i,r,s,a,u,c;for(null==o&&(o=n),a=function(){c=[];for(var n=0,o=e(t.length/2);o>=0?o>n:n>o;o>=0?n++:n--)c.push(n);return c}.apply(this).reverse(),u=[],r=0,s=a.length;s>r;r++)i=a[r],u.push(d(t,i,o));return u},p=function(t,e,o){var i;return null==o&&(o=n),i=t.indexOf(e),-1!==i?(f(t,0,i,o),d(t,i,o)):void 0},l=function(t,e,i){var r,a,u,c,l;if(null==i&&(i=n),a=t.slice(0,e),!a.length)return a;for(o(a,i),l=t.slice(e),u=0,c=l.length;c>u;u++)r=l[u],s(a,r,i);return a.sort(i).reverse()},h=function(t,e,r){var s,a,l,h,p,f,d,y,v,g;if(null==r&&(r=n),10*e<=t.length){if(h=t.slice(0,e).sort(r),!h.length)return h;for(l=h[h.length-1],y=t.slice(e),p=0,d=y.length;d>p;p++)s=y[p],r(s,l)<0&&(u(h,s,0,null,r),h.pop(),l=h[h.length-1]);return h}for(o(t,r),g=[],a=f=0,v=c(e,t.length);v>=0?v>f:f>v;a=v>=0?++f:--f)g.push(i(t,r));return g},f=function(t,e,o,i){var r,s,a;for(null==i&&(i=n),r=t[o];o>e&&(a=o-1>>1,s=t[a],i(r,s)<0);)t[o]=s,o=a;return t[o]=r},d=function(t,e,o){var i,r,s,a,u;for(null==o&&(o=n),r=t.length,u=e,s=t[e],i=2*e+1;r>i;)a=i+1,r>a&&!(o(t[i],t[a])<0)&&(i=a),t[e]=t[i],e=i,i=2*e+1;return t[e]=s,f(t,u,e,o)},t=function(){function t(t){this.cmp=null!=t?t:n,this.nodes=[]}return t.push=r,t.pop=i,t.replace=a,t.pushpop=s,t.heapify=o,t.updateItem=p,t.nlargest=l,t.nsmallest=h,t.prototype.push=function(t){return r(this.nodes,t,this.cmp)},t.prototype.pop=function(){return i(this.nodes,this.cmp)},t.prototype.peek=function(){return this.nodes[0]},t.prototype.contains=function(t){return-1!==this.nodes.indexOf(t)},t.prototype.replace=function(t){return a(this.nodes,t,this.cmp)},t.prototype.pushpop=function(t){return s(this.nodes,t,this.cmp)},t.prototype.heapify=function(){return o(this.nodes,this.cmp)},t.prototype.updateItem=function(t){return p(this.nodes,t,this.cmp)},t.prototype.clear=function(){return this.nodes=[]},t.prototype.empty=function(){return 0===this.nodes.length},t.prototype.size=function(){return this.nodes.length},t.prototype.clone=function(){var n;return n=new t,n.nodes=this.nodes.slice(0),n},t.prototype.toArray=function(){return this.nodes.slice(0)},t.prototype.insert=t.prototype.push,t.prototype.top=t.prototype.peek,t.prototype.front=t.prototype.peek,t.prototype.has=t.prototype.contains,t.prototype.copy=t.prototype.clone,t}(),function(t,n){return"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?module.exports=n():t.Heap=n()}(this,function(){return t})}).call(this);var EasyStar=EasyStar||{};"function"==typeof define&&define.amd&&define("easystar",[],function(){return EasyStar}),"undefined"!=typeof module&&module.exports&&(module.exports=EasyStar),EasyStar.Node=function(t,n,e,o,i){this.parent=t,this.x=n,this.y=e,this.costSoFar=o,this.simpleDistanceToTarget=i,this.bestGuessDistance=function(){return this.costSoFar+this.simpleDistanceToTarget}},EasyStar.Node.OPEN_LIST=0,EasyStar.Node.CLOSED_LIST=1,EasyStar.instance=function(){this.isDoneCalculating=!0,this.pointsToAvoid={},this.startX,this.callback,this.startY,this.endX,this.endY,this.nodeHash={},this.openList},EasyStar.js=function(){var t,n,e,o=1,i=1.4,r=!1,s={},a={},u={},c=!0,l=[],h=Number.MAX_VALUE,p=!1;this.setAcceptableTiles=function(t){t instanceof Array?e=t:!isNaN(parseFloat(t))&&isFinite(t)&&(e=[t])},this.enableSync=function(){r=!0},this.disableSync=function(){r=!1},this.enableDiagonals=function(){p=!0},this.disableDiagonals=function(){p=!1},this.setGrid=function(n){t=n;for(var e=0;en||0>i||0>s||0>a||n>t[0].length-1||i>t.length-1||s>t[0].length-1||a>t.length-1)throw new Error("Your start or end point is outside the scope of your grid.");if(n===s&&i===a)return c([]),void 0;for(var h=t[a][s],p=!1,f=0;fn;n++){if(0===l.length)return;if(r&&(n=0),0!==l[0].openList.size()){var s=l[0].openList.pop(),a=[];s.list=EasyStar.Node.CLOSED_LIST,s.y>0&&a.push({instance:l[0],searchNode:s,x:0,y:-1,cost:o*y(s.x,s.y-1)}),s.x0&&a.push({instance:l[0],searchNode:s,x:-1,y:0,cost:o*y(s.x-1,s.y)}),p&&(s.x>0&&s.y>0&&(c||d(t,e,s.x,s.y-1)&&d(t,e,s.x-1,s.y))&&a.push({instance:l[0],searchNode:s,x:-1,y:-1,cost:i*y(s.x-1,s.y-1)}),s.x0&&(c||d(t,e,s.x,s.y-1)&&d(t,e,s.x+1,s.y))&&a.push({instance:l[0],searchNode:s,x:1,y:-1,cost:i*y(s.x+1,s.y-1)}),s.x>0&&s.ye?-1:e===o?0:1});for(var u=!1,v=0;vr?i*r+s:i*s+r}var r=Math.abs(t-e),s=Math.abs(n-o);return r+s}}; \ No newline at end of file +var EasyStar=function(t){function n(o){if(e[o])return e[o].exports;var i=e[o]={exports:{},id:o,loaded:!1};return t[o].call(i.exports,i,i.exports,n),i.loaded=!0,i.exports}var e={};return n.m=t,n.c=e,n.p="",n(0)}([function(t,n,e){var o={},i=e(1),r=e(2),s=e(3);const a=0,u=1;t.exports=o,o.js=function(){var t,n,e,o=1,c=1.4,l=!1,h={},p={},f={},d=!0,y=[],v=Number.MAX_VALUE,g=!1;this.setAcceptableTiles=function(t){t instanceof Array?e=t:!isNaN(parseFloat(t))&&isFinite(t)&&(e=[t])},this.enableSync=function(){l=!0},this.disableSync=function(){l=!1},this.enableDiagonals=function(){g=!0},this.disableDiagonals=function(){g=!1},this.setGrid=function(n){t=n;for(var e=0;en||0>r||0>a||0>u||n>t[0].length-1||r>t.length-1||a>t[0].length-1||u>t.length-1)throw new Error("Your start or end point is outside the scope of your grid.");if(n===a&&r===u)return void h([]);for(var p=t[u][a],f=!1,d=0;dn;n++){if(0===y.length)return;if(l&&(n=0),0!==y[0].openList.size()){var i=y[0].openList.pop();if(y[0].endX===i.x&&y[0].endY===i.y){y[0].isDoneCalculating=!0;var r=[];r.push({x:i.x,y:i.y});for(var s=i.parent;null!=s;)r.push({x:s.x,y:s.y}),s=s.parent;r.reverse();var u=y[0],h=r;return void u.callback(h)}var p=[];i.list=a,i.y>0&&p.push({instance:y[0],searchNode:i,x:0,y:-1,cost:o*m(i.x,i.y-1)}),i.x0&&p.push({instance:y[0],searchNode:i,x:-1,y:0,cost:o*m(i.x-1,i.y)}),g&&(i.x>0&&i.y>0&&(d||b(t,e,i.x,i.y-1)&&b(t,e,i.x-1,i.y))&&p.push({instance:y[0],searchNode:i,x:-1,y:-1,cost:c*m(i.x-1,i.y-1)}),i.x0&&(d||b(t,e,i.x,i.y-1)&&b(t,e,i.x+1,i.y))&&p.push({instance:y[0],searchNode:i,x:1,y:-1,cost:c*m(i.x+1,i.y-1)}),i.x>0&&i.yi?c*i+r:c*r+i}var i=Math.abs(t-e),r=Math.abs(n-o);return i+r}}},function(t,n){t.exports=function(){this.isDoneCalculating=!0,this.pointsToAvoid={},this.startX,this.callback,this.startY,this.endX,this.endY,this.nodeHash={},this.openList}},function(t,n){t.exports=function(t,n,e,o,i){this.parent=t,this.x=n,this.y=e,this.costSoFar=o,this.simpleDistanceToTarget=i,this.bestGuessDistance=function(){return this.costSoFar+this.simpleDistanceToTarget}}},function(t,n,e){t.exports=e(4)},function(t,n,e){var o,i,r;(function(){var e,s,a,u,c,l,h,p,f,d,y,v,g,x,b;a=Math.floor,d=Math.min,s=function(t,n){return n>t?-1:t>n?1:0},f=function(t,n,e,o,i){var r;if(null==e&&(e=0),null==i&&(i=s),0>e)throw new Error("lo must be non-negative");for(null==o&&(o=t.length);o>e;)r=a((e+o)/2),i(n,t[r])<0?o=r:e=r+1;return[].splice.apply(t,[e,e-e].concat(n)),n},l=function(t,n,e){return null==e&&(e=s),t.push(n),x(t,0,t.length-1,e)},c=function(t,n){var e,o;return null==n&&(n=s),e=t.pop(),t.length?(o=t[0],t[0]=e,b(t,0,n)):o=e,o},p=function(t,n,e){var o;return null==e&&(e=s),o=t[0],t[0]=n,b(t,0,e),o},h=function(t,n,e){var o;return null==e&&(e=s),t.length&&e(t[0],n)<0&&(o=[t[0],n],n=o[0],t[0]=o[1],b(t,0,e)),n},u=function(t,n){var e,o,i,r,u,c;for(null==n&&(n=s),r=function(){c=[];for(var n=0,e=a(t.length/2);e>=0?e>n:n>e;e>=0?n++:n--)c.push(n);return c}.apply(this).reverse(),u=[],o=0,i=r.length;i>o;o++)e=r[o],u.push(b(t,e,n));return u},g=function(t,n,e){var o;return null==e&&(e=s),o=t.indexOf(n),-1!==o?(x(t,0,o,e),b(t,o,e)):void 0},y=function(t,n,e){var o,i,r,a,c;if(null==e&&(e=s),i=t.slice(0,n),!i.length)return i;for(u(i,e),c=t.slice(n),r=0,a=c.length;a>r;r++)o=c[r],h(i,o,e);return i.sort(e).reverse()},v=function(t,n,e){var o,i,r,a,l,h,p,y,v,g;if(null==e&&(e=s),10*n<=t.length){if(a=t.slice(0,n).sort(e),!a.length)return a;for(r=a[a.length-1],y=t.slice(n),l=0,p=y.length;p>l;l++)o=y[l],e(o,r)<0&&(f(a,o,0,null,e),a.pop(),r=a[a.length-1]);return a}for(u(t,e),g=[],i=h=0,v=d(n,t.length);v>=0?v>h:h>v;i=v>=0?++h:--h)g.push(c(t,e));return g},x=function(t,n,e,o){var i,r,a;for(null==o&&(o=s),i=t[e];e>n&&(a=e-1>>1,r=t[a],o(i,r)<0);)t[e]=r,e=a;return t[e]=i},b=function(t,n,e){var o,i,r,a,u;for(null==e&&(e=s),i=t.length,u=n,r=t[n],o=2*n+1;i>o;)a=o+1,i>a&&!(e(t[o],t[a])<0)&&(o=a),t[n]=t[o],n=o,o=2*n+1;return t[n]=r,x(t,u,n,e)},e=function(){function t(t){this.cmp=null!=t?t:s,this.nodes=[]}return t.push=l,t.pop=c,t.replace=p,t.pushpop=h,t.heapify=u,t.updateItem=g,t.nlargest=y,t.nsmallest=v,t.prototype.push=function(t){return l(this.nodes,t,this.cmp)},t.prototype.pop=function(){return c(this.nodes,this.cmp)},t.prototype.peek=function(){return this.nodes[0]},t.prototype.contains=function(t){return-1!==this.nodes.indexOf(t)},t.prototype.replace=function(t){return p(this.nodes,t,this.cmp)},t.prototype.pushpop=function(t){return h(this.nodes,t,this.cmp)},t.prototype.heapify=function(){return u(this.nodes,this.cmp)},t.prototype.updateItem=function(t){return g(this.nodes,t,this.cmp)},t.prototype.clear=function(){return this.nodes=[]},t.prototype.empty=function(){return 0===this.nodes.length},t.prototype.size=function(){return this.nodes.length},t.prototype.clone=function(){var n;return n=new t,n.nodes=this.nodes.slice(0),n},t.prototype.toArray=function(){return this.nodes.slice(0)},t.prototype.insert=t.prototype.push,t.prototype.top=t.prototype.peek,t.prototype.front=t.prototype.peek,t.prototype.has=t.prototype.contains,t.prototype.copy=t.prototype.clone,t}(),function(e,s){return i=[],o=s,r="function"==typeof o?o.apply(n,i):o,!(void 0!==r&&(t.exports=r))}(this,function(){return e})}).call(this)}]); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index fb5b750..0000000 --- a/gulpfile.js +++ /dev/null @@ -1,36 +0,0 @@ -var gulp = require('gulp'); -var concat = require('gulp-concat'); -var config = require('./package.json'); -var sources = ['node_modules/heap/lib/heap.js', 'src/package-managers.js', 'src/node.js', 'src/priority-queue.js', 'src/instance.js', 'src/easystar.js']; -var uglify = require('gulp-uglify'); -var karma = require('gulp-karma'); - -gulp.task('build', function() { - gulp.run('concat', 'minify'); -}); - -gulp.task('concat', function() { - gulp.src(sources) - .pipe(concat('easystar-' + config.version + '.js')) - .pipe(gulp.dest('bin')); -}); - -gulp.task('minify', function() { - gulp.src(sources) - .pipe(concat('easystar-' + config.version + '.min.js')) - .pipe(uglify()) - .pipe(gulp.dest('bin')); -}); - -gulp.task('test', function() { - // Be sure to return the stream - return gulp.src(['bin/easystar-' + config.version + '.js', 'test/*.js']) - .pipe(karma({ - configFile: 'karma.conf.js', - action: 'run' - })) - .on('error', function(err) { - // Make sure failed tests cause gulp to exit non-zero - throw err; - }); -}); \ No newline at end of file diff --git a/karma.conf.js b/karma.conf.js index 99c4bb1..8b4762f 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -5,7 +5,7 @@ module.exports = function(config) { config.set({ // base path, that will be used to resolve files and exclude - basePath: '', + basePath: './', plugins: [ 'karma-jasmine', @@ -20,7 +20,8 @@ module.exports = function(config) { // list of files / patterns to load in the browser files: [ - + './bin/easystar-0.2.3.js', + './test/easystartest.js' ], diff --git a/package.json b/package.json index 339d531..c8123df 100644 --- a/package.json +++ b/package.json @@ -1,36 +1,43 @@ { - "name": "easystarjs", - "version": "0.2.3", - "main": "./bin/easystar-0.2.3", - "description": "Asynchronous A* Pathfinding API", - "keywords": [ - "A*", - "pathfinding", - "grid", - "easystar" - ], - "homepage": "http://www.easystarjs.com", - "repository": { - "type": "git", - "url": "git://github.com/prettymuchbryce/easystarjs.git" - }, - "license": "MIT", - "author": { - "name": "Bryce Neal", - "email": "brycedneal@gmail.com", - "url": "http://bryce.is" - }, - "devDependencies": { - "gulp": "^3.8.0", - "gulp-concat": "^2.2.0", - "gulp-karma": "0.0.4", - "gulp-uglify": "^0.3.0", - "karma": "^0.12.16", - "karma-coverage": "^0.2.4", - "karma-jasmine": "^0.3.5", - "karma-phantomjs-launcher": "^0.1.4" - }, - "dependencies": { - "heap": "^0.2.6" - } + "name": "easystarjs", + "version": "0.2.3", + "main": "./src/easystar.js", + "description": "Asynchronous A* Pathfinding API", + "scripts": { + "build": "webpack && webpack --minify", + "test": "./node_modules/karma/bin/karma start ./karma.conf.js" + }, + "keywords": [ + "A*", + "pathfinding", + "grid", + "easystar" + ], + "homepage": "http://www.easystarjs.com", + "repository": { + "type": "git", + "url": "git://github.com/prettymuchbryce/easystarjs.git" + }, + "license": "MIT", + "author": { + "name": "Bryce Neal", + "email": "brycedneal@gmail.com", + "url": "http://bryce.is" + }, + "devDependencies": { + "gulp": "^3.8.0", + "gulp-concat": "^2.2.0", + "gulp-karma": "0.0.4", + "gulp-uglify": "^0.3.0", + "karma": "^0.12.16", + "karma-coverage": "^0.2.4", + "karma-jasmine": "^0.3.5", + "karma-phantomjs-launcher": "^0.1.4" + }, + "dependencies": { + "babel-core": "^6.8.0", + "babel-loader": "^6.2.4", + "heap": "^0.2.6", + "webpack": "^1.13.0" + } } diff --git a/src/easystar.js b/src/easystar.js index 03868cf..e252284 100755 --- a/src/easystar.js +++ b/src/easystar.js @@ -1,460 +1,470 @@ /** -* EasyStar.js -* github.com/prettymuchbryce/EasyStarJS -* Licensed under the MIT license. +* EasyStar.js +* github.com/prettymuchbryce/EasyStarJS +* Licensed under the MIT license. * -* Implementation By Bryce Neal (@prettymuchbryce) +* Implementation By Bryce Neal (@prettymuchbryce) **/ + +var EasyStar = {} +var Instance = require('./instance'); +var Node = require('./node'); +var Heap = require('heap'); + +const CLOSED_LIST = 0; +const OPEN_LIST = 1; + +module.exports = EasyStar; + EasyStar.js = function() { - var STRAIGHT_COST = 1.0; - var DIAGONAL_COST = 1.4; - var syncEnabled = false; - var pointsToAvoid = {}; - var collisionGrid; - var costMap = {}; - var pointsToCost = {}; - var allowCornerCutting = true; - var iterationsSoFar; - var instances = []; - var iterationsPerCalculation = Number.MAX_VALUE; - var acceptableTiles; - var diagonalsEnabled = false; - - /** - * Sets the collision grid that EasyStar uses. - * - * @param {Array|Number} tiles An array of numbers that represent - * which tiles in your grid should be considered - * acceptable, or "walkable". - **/ - this.setAcceptableTiles = function(tiles) { - if (tiles instanceof Array) { - // Array - acceptableTiles = tiles; - } else if (!isNaN(parseFloat(tiles)) && isFinite(tiles)) { - // Number - acceptableTiles = [tiles]; - } - }; - - /** - * Enables sync mode for this EasyStar instance.. - * if you're into that sort of thing. - **/ - this.enableSync = function() { - syncEnabled = true; - }; - - /** - * Disables sync mode for this EasyStar instance. - **/ - this.disableSync = function() { - syncEnabled = false; - }; - - /** - * Enable diagonal pathfinding. - */ - this.enableDiagonals = function() { - diagonalsEnabled = true; - } - - /** - * Disable diagonal pathfinding. - */ - this.disableDiagonals = function() { - diagonalsEnabled = false; - } - - /** - * Sets the collision grid that EasyStar uses. - * - * @param {Array} grid The collision grid that this EasyStar instance will read from. - * This should be a 2D Array of Numbers. - **/ - this.setGrid = function(grid) { - collisionGrid = grid; - - //Setup cost map - for (var y = 0; y < collisionGrid.length; y++) { - for (var x = 0; x < collisionGrid[0].length; x++) { - if (!costMap[collisionGrid[y][x]]) { - costMap[collisionGrid[y][x]] = 1 - } - } - } - }; - - /** - * Sets the tile cost for a particular tile type. - * - * @param {Number} The tile type to set the cost for. - * @param {Number} The multiplicative cost associated with the given tile. - **/ - this.setTileCost = function(tileType, cost) { - costMap[tileType] = cost; - }; - - /** - * Sets the an additional cost for a particular point. - * Overrides the cost from setTileCost. - * - * @param {Number} x The x value of the point to cost. - * @param {Number} y The y value of the point to cost. - * @param {Number} The multiplicative cost associated with the given point. - **/ - this.setAdditionalPointCost = function(x, y, cost) { - pointsToCost[x + '_' + y] = cost; - }; - - /** - * Remove the additional cost for a particular point. - * - * @param {Number} x The x value of the point to stop costing. - * @param {Number} y The y value of the point to stop costing. - **/ - this.removeAdditionalPointCost = function(x, y) { - delete pointsToCost[x + '_' + y]; - } - - /** - * Remove all additional point costs. - **/ - this.removeAllAdditionalPointCosts = function() { - pointsToCost = {}; - } - - /** - * Sets the number of search iterations per calculation. - * A lower number provides a slower result, but more practical if you - * have a large tile-map and don't want to block your thread while - * finding a path. - * - * @param {Number} iterations The number of searches to prefrom per calculate() call. - **/ - this.setIterationsPerCalculation = function(iterations) { - iterationsPerCalculation = iterations; - }; - - /** - * Avoid a particular point on the grid, - * regardless of whether or not it is an acceptable tile. - * - * @param {Number} x The x value of the point to avoid. - * @param {Number} y The y value of the point to avoid. - **/ - this.avoidAdditionalPoint = function(x, y) { - pointsToAvoid[x + "_" + y] = 1; - }; - - /** - * Stop avoiding a particular point on the grid. - * - * @param {Number} x The x value of the point to stop avoiding. - * @param {Number} y The y value of the point to stop avoiding. - **/ - this.stopAvoidingAdditionalPoint = function(x, y) { - delete pointsToAvoid[x + "_" + y]; - }; - - /** - * Enables corner cutting in diagonal movement. - **/ - this.enableCornerCutting = function() { - allowCornerCutting = true; - }; - - /** - * Disables corner cutting in diagonal movement. - **/ - this.disableCornerCutting = function() { - allowCornerCutting = false; - }; - - /** - * Stop avoiding all additional points on the grid. - **/ - this.stopAvoidingAllAdditionalPoints = function() { - pointsToAvoid = {}; - }; - - /** - * Find a path. - * - * @param {Number} startX The X position of the starting point. - * @param {Number} startY The Y position of the starting point. - * @param {Number} endX The X position of the ending point. - * @param {Number} endY The Y position of the ending point. - * @param {Function} callback A function that is called when your path - * is found, or no path is found. - * - **/ - this.findPath = function(startX, startY, endX, endY, callback) { - // Wraps the callback for sync vs async logic - var callbackWrapper = function(result) { - if (syncEnabled) { - callback(result); - } else { - setTimeout(function() { - callback(result); - }); - } - } - - // No acceptable tiles were set - if (acceptableTiles === undefined) { - throw new Error("You can't set a path without first calling setAcceptableTiles() on EasyStar."); - } - // No grid was set - if (collisionGrid === undefined) { - throw new Error("You can't set a path without first calling setGrid() on EasyStar."); - } - - // Start or endpoint outside of scope. - if (startX < 0 || startY < 0 || endX < 0 || endY < 0 || - startX > collisionGrid[0].length-1 || startY > collisionGrid.length-1 || - endX > collisionGrid[0].length-1 || endY > collisionGrid.length-1) { - throw new Error("Your start or end point is outside the scope of your grid."); - } - - // Start and end are the same tile. - if (startX===endX && startY===endY) { - callbackWrapper([]); - return; - } - - // End point is not an acceptable tile. - var endTile = collisionGrid[endY][endX]; - var isAcceptable = false; - for (var i = 0; i < acceptableTiles.length; i++) { - if (endTile === acceptableTiles[i]) { - isAcceptable = true; - break; - } - } - - if (isAcceptable === false) { - callbackWrapper(null); - return; - } - - // Create the instance - var instance = new EasyStar.instance(); - instance.openList = new Heap(function(nodeA, nodeB) { - return nodeA.bestGuessDistance() - nodeB.bestGuessDistance(); - }) - instance.isDoneCalculating = false; - instance.nodeHash = {}; - instance.startX = startX; - instance.startY = startY; - instance.endX = endX; - instance.endY = endY; - instance.callback = callbackWrapper; - - instance.openList.push(coordinateToNode(instance, instance.startX, - instance.startY, null, STRAIGHT_COST)); - - instances.push(instance); - }; - - /** - * This method steps through the A* Algorithm in an attempt to - * find your path(s). It will search 4-8 tiles (depending on diagonals) for every calculation. - * You can change the number of calculations done in a call by using - * easystar.setIteratonsPerCalculation(). - **/ - this.calculate = function() { - if (instances.length === 0 || collisionGrid === undefined || acceptableTiles === undefined) { - return; - } - for (iterationsSoFar = 0; iterationsSoFar < iterationsPerCalculation; iterationsSoFar++) { - if (instances.length === 0) { - return; - } - - if (syncEnabled) { - // If this is a sync instance, we want to make sure that it calculates synchronously. - iterationsSoFar = 0; - } - - // Couldn't find a path. - if (instances[0].openList.size() === 0) { - var ic = instances[0]; - ic.callback(null); - instances.shift(); - continue; - } - - var searchNode = instances[0].openList.pop(); - - // Handles the case where we have found the destination - if (instances[0].endX === searchNode.x && instances[0].endY === searchNode.y) { - instances[0].isDoneCalculating = true; - var path = []; - path.push(x: searchNode.x, y: searchNode.y); - var parent = searchNode.parent; - while (parent!=null) { - path.push({x: parent.x, y:parent.y}); - parent = parent.parent; - } - path.reverse(); - var ic = instances[0]; - var ip = path; - ic.callback(ip); - return - } - - var tilesToSearch = []; - searchNode.list = EasyStar.Node.CLOSED_LIST; - - if (searchNode.y > 0) { - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: 0, y: -1, cost: STRAIGHT_COST * getTileCost(searchNode.x, searchNode.y-1)}); - } - if (searchNode.x < collisionGrid[0].length-1) { - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: 1, y: 0, cost: STRAIGHT_COST * getTileCost(searchNode.x+1, searchNode.y)}); - } - if (searchNode.y < collisionGrid.length-1) { - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: 0, y: 1, cost: STRAIGHT_COST * getTileCost(searchNode.x, searchNode.y+1)}); - } - if (searchNode.x > 0) { - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: -1, y: 0, cost: STRAIGHT_COST * getTileCost(searchNode.x-1, searchNode.y)}); - } - if (diagonalsEnabled) { - if (searchNode.x > 0 && searchNode.y > 0) { - - if (allowCornerCutting || - (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y-1) && - isTileWalkable(collisionGrid, acceptableTiles, searchNode.x-1, searchNode.y))) { - - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: -1, y: -1, cost: DIAGONAL_COST * getTileCost(searchNode.x-1, searchNode.y-1)}); - } - } - if (searchNode.x < collisionGrid[0].length-1 && searchNode.y < collisionGrid.length-1) { - - if (allowCornerCutting || - (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y+1) && - isTileWalkable(collisionGrid, acceptableTiles, searchNode.x+1, searchNode.y))) { - - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: 1, y: 1, cost: DIAGONAL_COST * getTileCost(searchNode.x+1, searchNode.y+1)}); - } - } - if (searchNode.x < collisionGrid[0].length-1 && searchNode.y > 0) { - - if (allowCornerCutting || - (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y-1) && - isTileWalkable(collisionGrid, acceptableTiles, searchNode.x+1, searchNode.y))) { - - - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: 1, y: -1, cost: DIAGONAL_COST * getTileCost(searchNode.x+1, searchNode.y-1)}); - } - } - if (searchNode.x > 0 && searchNode.y < collisionGrid.length-1) { - - if (allowCornerCutting || - (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y+1) && - isTileWalkable(collisionGrid, acceptableTiles, searchNode.x-1, searchNode.y))) { - - - tilesToSearch.push({ instance: instances[0], searchNode: searchNode, - x: -1, y: 1, cost: DIAGONAL_COST * getTileCost(searchNode.x-1, searchNode.y+1)}); - } - } - } - - var isDoneCalculating = false; - - // Search all of the surrounding nodes - for (var i = 0; i < tilesToSearch.length; i++) { - checkAdjacentNode(tilesToSearch[i].instance, tilesToSearch[i].searchNode, - tilesToSearch[i].x, tilesToSearch[i].y, tilesToSearch[i].cost); - if (tilesToSearch[i].instance.isDoneCalculating === true) { - isDoneCalculating = true; - break; - } - } - - if (isDoneCalculating) { - instances.shift(); - continue; - } - - } - }; - - // Private methods follow - var checkAdjacentNode = function(instance, searchNode, x, y, cost) { - var adjacentCoordinateX = searchNode.x+x; - var adjacentCoordinateY = searchNode.y+y; - - if (pointsToAvoid[adjacentCoordinateX + "_" + adjacentCoordinateY] === undefined && - isTileWalkable(collisionGrid, acceptableTiles, adjacentCoordinateX, adjacentCoordinateY)) { - var node = coordinateToNode(instance, adjacentCoordinateX, - adjacentCoordinateY, searchNode, cost); - - if (node.list === undefined) { - node.list = EasyStar.Node.OPEN_LIST; - instance.openList.push(node); - } else if (searchNode.costSoFar + cost < node.costSoFar) { - node.costSoFar = searchNode.costSoFar + cost; - node.parent = searchNode; - instance.openList.updateItem(node); - } - } - } - }; - - // Helpers - var isTileWalkable = function(collisionGrid, acceptableTiles, x, y) { - for (var i = 0; i < acceptableTiles.length; i++) { - if (collisionGrid[y][x] === acceptableTiles[i]) { - return true; - } - } - - return false; - }; - - var getTileCost = function(x, y) { - return pointsToCost[x + '_' + y] || costMap[collisionGrid[y][x]] - }; - - var coordinateToNode = function(instance, x, y, parent, cost) { - if (instance.nodeHash[x + "_" + y]!==undefined) { - return instance.nodeHash[x + "_" + y]; - } - var simpleDistanceToTarget = getDistance(x, y, instance.endX, instance.endY); - if (parent!==null) { - var costSoFar = parent.costSoFar + cost; - } else { - costSoFar = 0; - } - var node = new EasyStar.Node(parent,x,y,costSoFar,simpleDistanceToTarget); - instance.nodeHash[x + "_" + y] = node; - return node; - }; - - var getDistance = function(x1,y1,x2,y2) { - if (diagonalsEnabled) { - // Octile distance - var dx = Math.abs(x1 - x2); - var dy = Math.abs(y1 - y2); - if (dx < dy) { - return DIAGONAL_COST * dx + dy; - } else { - return DIAGONAL_COST * dy + dx; - } - } else { - // Manhattan distance - var dx = Math.abs(x1 - x2); - var dy = Math.abs(y1 - y2); - return (dx + dy) - } - }; + var STRAIGHT_COST = 1.0; + var DIAGONAL_COST = 1.4; + var syncEnabled = false; + var pointsToAvoid = {}; + var collisionGrid; + var costMap = {}; + var pointsToCost = {}; + var allowCornerCutting = true; + var iterationsSoFar; + var instances = []; + var iterationsPerCalculation = Number.MAX_VALUE; + var acceptableTiles; + var diagonalsEnabled = false; + + /** + * Sets the collision grid that EasyStar uses. + * + * @param {Array|Number} tiles An array of numbers that represent + * which tiles in your grid should be considered + * acceptable, or "walkable". + **/ + this.setAcceptableTiles = function(tiles) { + if (tiles instanceof Array) { + // Array + acceptableTiles = tiles; + } else if (!isNaN(parseFloat(tiles)) && isFinite(tiles)) { + // Number + acceptableTiles = [tiles]; + } + }; + + /** + * Enables sync mode for this EasyStar instance.. + * if you're into that sort of thing. + **/ + this.enableSync = function() { + syncEnabled = true; + }; + + /** + * Disables sync mode for this EasyStar instance. + **/ + this.disableSync = function() { + syncEnabled = false; + }; + + /** + * Enable diagonal pathfinding. + */ + this.enableDiagonals = function() { + diagonalsEnabled = true; + } + + /** + * Disable diagonal pathfinding. + */ + this.disableDiagonals = function() { + diagonalsEnabled = false; + } + + /** + * Sets the collision grid that EasyStar uses. + * + * @param {Array} grid The collision grid that this EasyStar instance will read from. + * This should be a 2D Array of Numbers. + **/ + this.setGrid = function(grid) { + collisionGrid = grid; + + //Setup cost map + for (var y = 0; y < collisionGrid.length; y++) { + for (var x = 0; x < collisionGrid[0].length; x++) { + if (!costMap[collisionGrid[y][x]]) { + costMap[collisionGrid[y][x]] = 1 + } + } + } + }; + + /** + * Sets the tile cost for a particular tile type. + * + * @param {Number} The tile type to set the cost for. + * @param {Number} The multiplicative cost associated with the given tile. + **/ + this.setTileCost = function(tileType, cost) { + costMap[tileType] = cost; + }; + + /** + * Sets the an additional cost for a particular point. + * Overrides the cost from setTileCost. + * + * @param {Number} x The x value of the point to cost. + * @param {Number} y The y value of the point to cost. + * @param {Number} The multiplicative cost associated with the given point. + **/ + this.setAdditionalPointCost = function(x, y, cost) { + pointsToCost[x + '_' + y] = cost; + }; + + /** + * Remove the additional cost for a particular point. + * + * @param {Number} x The x value of the point to stop costing. + * @param {Number} y The y value of the point to stop costing. + **/ + this.removeAdditionalPointCost = function(x, y) { + delete pointsToCost[x + '_' + y]; + } + + /** + * Remove all additional point costs. + **/ + this.removeAllAdditionalPointCosts = function() { + pointsToCost = {}; + } + + /** + * Sets the number of search iterations per calculation. + * A lower number provides a slower result, but more practical if you + * have a large tile-map and don't want to block your thread while + * finding a path. + * + * @param {Number} iterations The number of searches to prefrom per calculate() call. + **/ + this.setIterationsPerCalculation = function(iterations) { + iterationsPerCalculation = iterations; + }; + + /** + * Avoid a particular point on the grid, + * regardless of whether or not it is an acceptable tile. + * + * @param {Number} x The x value of the point to avoid. + * @param {Number} y The y value of the point to avoid. + **/ + this.avoidAdditionalPoint = function(x, y) { + pointsToAvoid[x + "_" + y] = 1; + }; + + /** + * Stop avoiding a particular point on the grid. + * + * @param {Number} x The x value of the point to stop avoiding. + * @param {Number} y The y value of the point to stop avoiding. + **/ + this.stopAvoidingAdditionalPoint = function(x, y) { + delete pointsToAvoid[x + "_" + y]; + }; + + /** + * Enables corner cutting in diagonal movement. + **/ + this.enableCornerCutting = function() { + allowCornerCutting = true; + }; + + /** + * Disables corner cutting in diagonal movement. + **/ + this.disableCornerCutting = function() { + allowCornerCutting = false; + }; + + /** + * Stop avoiding all additional points on the grid. + **/ + this.stopAvoidingAllAdditionalPoints = function() { + pointsToAvoid = {}; + }; + + /** + * Find a path. + * + * @param {Number} startX The X position of the starting point. + * @param {Number} startY The Y position of the starting point. + * @param {Number} endX The X position of the ending point. + * @param {Number} endY The Y position of the ending point. + * @param {Function} callback A function that is called when your path + * is found, or no path is found. + * + **/ + this.findPath = function(startX, startY, endX, endY, callback) { + // Wraps the callback for sync vs async logic + var callbackWrapper = function(result) { + if (syncEnabled) { + callback(result); + } else { + setTimeout(function() { + callback(result); + }); + } + } + + // No acceptable tiles were set + if (acceptableTiles === undefined) { + throw new Error("You can't set a path without first calling setAcceptableTiles() on EasyStar."); + } + // No grid was set + if (collisionGrid === undefined) { + throw new Error("You can't set a path without first calling setGrid() on EasyStar."); + } + + // Start or endpoint outside of scope. + if (startX < 0 || startY < 0 || endX < 0 || endY < 0 || + startX > collisionGrid[0].length-1 || startY > collisionGrid.length-1 || + endX > collisionGrid[0].length-1 || endY > collisionGrid.length-1) { + throw new Error("Your start or end point is outside the scope of your grid."); + } + + // Start and end are the same tile. + if (startX===endX && startY===endY) { + callbackWrapper([]); + return; + } + + // End point is not an acceptable tile. + var endTile = collisionGrid[endY][endX]; + var isAcceptable = false; + for (var i = 0; i < acceptableTiles.length; i++) { + if (endTile === acceptableTiles[i]) { + isAcceptable = true; + break; + } + } + + if (isAcceptable === false) { + callbackWrapper(null); + return; + } + + // Create the instance + var instance = new Instance(); + instance.openList = new Heap(function(nodeA, nodeB) { + return nodeA.bestGuessDistance() - nodeB.bestGuessDistance(); + }); + instance.isDoneCalculating = false; + instance.nodeHash = {}; + instance.startX = startX; + instance.startY = startY; + instance.endX = endX; + instance.endY = endY; + instance.callback = callbackWrapper; + + instance.openList.push(coordinateToNode(instance, instance.startX, + instance.startY, null, STRAIGHT_COST)); + + instances.push(instance); + }; + + /** + * This method steps through the A* Algorithm in an attempt to + * find your path(s). It will search 4-8 tiles (depending on diagonals) for every calculation. + * You can change the number of calculations done in a call by using + * easystar.setIteratonsPerCalculation(). + **/ + this.calculate = function() { + if (instances.length === 0 || collisionGrid === undefined || acceptableTiles === undefined) { + return; + } + for (iterationsSoFar = 0; iterationsSoFar < iterationsPerCalculation; iterationsSoFar++) { + if (instances.length === 0) { + return; + } + + if (syncEnabled) { + // If this is a sync instance, we want to make sure that it calculates synchronously. + iterationsSoFar = 0; + } + + // Couldn't find a path. + if (instances[0].openList.size() === 0) { + var ic = instances[0]; + ic.callback(null); + instances.shift(); + continue; + } + + var searchNode = instances[0].openList.pop(); + + // Handles the case where we have found the destination + if (instances[0].endX === searchNode.x && instances[0].endY === searchNode.y) { + instances[0].isDoneCalculating = true; + var path = []; + path.push({x: searchNode.x, y: searchNode.y}); + var parent = searchNode.parent; + while (parent!=null) { + path.push({x: parent.x, y:parent.y}); + parent = parent.parent; + } + path.reverse(); + var ic = instances[0]; + var ip = path; + ic.callback(ip); + return + } + + var tilesToSearch = []; + searchNode.list = CLOSED_LIST; + + if (searchNode.y > 0) { + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: 0, y: -1, cost: STRAIGHT_COST * getTileCost(searchNode.x, searchNode.y-1)}); + } + if (searchNode.x < collisionGrid[0].length-1) { + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: 1, y: 0, cost: STRAIGHT_COST * getTileCost(searchNode.x+1, searchNode.y)}); + } + if (searchNode.y < collisionGrid.length-1) { + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: 0, y: 1, cost: STRAIGHT_COST * getTileCost(searchNode.x, searchNode.y+1)}); + } + if (searchNode.x > 0) { + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: -1, y: 0, cost: STRAIGHT_COST * getTileCost(searchNode.x-1, searchNode.y)}); + } + if (diagonalsEnabled) { + if (searchNode.x > 0 && searchNode.y > 0) { + + if (allowCornerCutting || + (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y-1) && + isTileWalkable(collisionGrid, acceptableTiles, searchNode.x-1, searchNode.y))) { + + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: -1, y: -1, cost: DIAGONAL_COST * getTileCost(searchNode.x-1, searchNode.y-1)}); + } + } + if (searchNode.x < collisionGrid[0].length-1 && searchNode.y < collisionGrid.length-1) { + + if (allowCornerCutting || + (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y+1) && + isTileWalkable(collisionGrid, acceptableTiles, searchNode.x+1, searchNode.y))) { + + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: 1, y: 1, cost: DIAGONAL_COST * getTileCost(searchNode.x+1, searchNode.y+1)}); + } + } + if (searchNode.x < collisionGrid[0].length-1 && searchNode.y > 0) { + + if (allowCornerCutting || + (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y-1) && + isTileWalkable(collisionGrid, acceptableTiles, searchNode.x+1, searchNode.y))) { + + + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: 1, y: -1, cost: DIAGONAL_COST * getTileCost(searchNode.x+1, searchNode.y-1)}); + } + } + if (searchNode.x > 0 && searchNode.y < collisionGrid.length-1) { + + if (allowCornerCutting || + (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y+1) && + isTileWalkable(collisionGrid, acceptableTiles, searchNode.x-1, searchNode.y))) { + + + tilesToSearch.push({ instance: instances[0], searchNode: searchNode, + x: -1, y: 1, cost: DIAGONAL_COST * getTileCost(searchNode.x-1, searchNode.y+1)}); + } + } + } + + var isDoneCalculating = false; + + // Search all of the surrounding nodes + for (var i = 0; i < tilesToSearch.length; i++) { + checkAdjacentNode(tilesToSearch[i].instance, tilesToSearch[i].searchNode, + tilesToSearch[i].x, tilesToSearch[i].y, tilesToSearch[i].cost); + if (tilesToSearch[i].instance.isDoneCalculating === true) { + isDoneCalculating = true; + break; + } + } + + if (isDoneCalculating) { + instances.shift(); + continue; + } + + } + }; + + // Private methods follow + var checkAdjacentNode = function(instance, searchNode, x, y, cost) { + var adjacentCoordinateX = searchNode.x+x; + var adjacentCoordinateY = searchNode.y+y; + + if (pointsToAvoid[adjacentCoordinateX + "_" + adjacentCoordinateY] === undefined && + isTileWalkable(collisionGrid, acceptableTiles, adjacentCoordinateX, adjacentCoordinateY)) { + var node = coordinateToNode(instance, adjacentCoordinateX, + adjacentCoordinateY, searchNode, cost); + + if (node.list === undefined) { + node.list = OPEN_LIST; + instance.openList.push(node); + } else if (searchNode.costSoFar + cost < node.costSoFar) { + node.costSoFar = searchNode.costSoFar + cost; + node.parent = searchNode; + instance.openList.updateItem(node); + } + } + }; + + // Helpers + var isTileWalkable = function(collisionGrid, acceptableTiles, x, y) { + for (var i = 0; i < acceptableTiles.length; i++) { + if (collisionGrid[y][x] === acceptableTiles[i]) { + return true; + } + } + + return false; + }; + + var getTileCost = function(x, y) { + return pointsToCost[x + '_' + y] || costMap[collisionGrid[y][x]] + }; + + var coordinateToNode = function(instance, x, y, parent, cost) { + if (instance.nodeHash[x + "_" + y]!==undefined) { + return instance.nodeHash[x + "_" + y]; + } + var simpleDistanceToTarget = getDistance(x, y, instance.endX, instance.endY); + if (parent!==null) { + var costSoFar = parent.costSoFar + cost; + } else { + costSoFar = 0; + } + var node = new Node(parent,x,y,costSoFar,simpleDistanceToTarget); + instance.nodeHash[x + "_" + y] = node; + return node; + }; + + var getDistance = function(x1,y1,x2,y2) { + if (diagonalsEnabled) { + // Octile distance + var dx = Math.abs(x1 - x2); + var dy = Math.abs(y1 - y2); + if (dx < dy) { + return DIAGONAL_COST * dx + dy; + } else { + return DIAGONAL_COST * dy + dx; + } + } else { + // Manhattan distance + var dx = Math.abs(x1 - x2); + var dy = Math.abs(y1 - y2); + return (dx + dy); + } + }; } \ No newline at end of file diff --git a/src/instance.js b/src/instance.js index 9b8169a..4f9673d 100644 --- a/src/instance.js +++ b/src/instance.js @@ -2,14 +2,14 @@ * Represents a single instance of EasyStar. * A path that is in the queue to eventually be found. */ -EasyStar.instance = function() { - this.isDoneCalculating = true; - this.pointsToAvoid = {}; - this.startX; - this.callback; - this.startY; - this.endX; - this.endY; - this.nodeHash = {}; - this.openList; +module.exports = function() { + this.isDoneCalculating = true; + this.pointsToAvoid = {}; + this.startX; + this.callback; + this.startY; + this.endX; + this.endY; + this.nodeHash = {}; + this.openList; }; \ No newline at end of file diff --git a/src/node.js b/src/node.js index 1cf12aa..f9756c8 100755 --- a/src/node.js +++ b/src/node.js @@ -6,21 +6,17 @@ * @param {Number} costSoFar How far this node is in moves*cost from the start. * @param {Number} simpleDistanceToTarget Manhatten distance to the end point. **/ -EasyStar.Node = function(parent, x, y, costSoFar, simpleDistanceToTarget) { - this.parent = parent; - this.x = x; - this.y = y; - this.costSoFar = costSoFar; - this.simpleDistanceToTarget = simpleDistanceToTarget; +module.exports = function(parent, x, y, costSoFar, simpleDistanceToTarget) { + this.parent = parent; + this.x = x; + this.y = y; + this.costSoFar = costSoFar; + this.simpleDistanceToTarget = simpleDistanceToTarget; - /** - * @return {Number} Best guess distance of a cost using this node. - **/ - this.bestGuessDistance = function() { - return this.costSoFar + this.simpleDistanceToTarget; - } -}; - -// Constants -EasyStar.Node.OPEN_LIST = 0; -EasyStar.Node.CLOSED_LIST = 1; \ No newline at end of file + /** + * @return {Number} Best guess distance of a cost using this node. + **/ + this.bestGuessDistance = function() { + return this.costSoFar + this.simpleDistanceToTarget; + } +}; \ No newline at end of file diff --git a/src/package-managers.js b/src/package-managers.js deleted file mode 100644 index 79597b5..0000000 --- a/src/package-managers.js +++ /dev/null @@ -1,14 +0,0 @@ -// NameSpace -var EasyStar = EasyStar || {}; - -// For require.js -if (typeof define === "function" && define.amd) { - define("easystar", [], function() { - return EasyStar; - }); -} - -// For browserify and node.js -if (typeof module !== 'undefined' && module.exports) { - module.exports = EasyStar; -} \ No newline at end of file diff --git a/test/easystartest.js b/test/easystartest.js index a515512..b32e565 100644 --- a/test/easystartest.js +++ b/test/easystartest.js @@ -3,248 +3,248 @@ describe("EasyStar.js", function() { beforeEach(function() { }); it("It should find a path successfully with corner cutting enabled.", function(done) { - var easyStar = new EasyStar.js(); - easyStar.enableDiagonals(); - var map = [[1,0,0,0,0], - [0,1,0,0,0], - [0,0,1,0,0], - [0,0,0,1,0], - [0,0,0,0,1]]; + var easyStar = new EasyStar.js(); + easyStar.enableDiagonals(); + var map = [[1,0,0,0,0], + [0,1,0,0,0], + [0,0,1,0,0], + [0,0,0,1,0], + [0,0,0,0,1]]; - easyStar.setGrid(map); + easyStar.setGrid(map); - easyStar.enableCornerCutting(); + easyStar.enableCornerCutting(); - easyStar.setAcceptableTiles([1]); + easyStar.setAcceptableTiles([1]); - easyStar.findPath(0,0,4,4,onPathFound); + easyStar.findPath(0,0,4,4,onPathFound); - easyStar.calculate(); + easyStar.calculate(); - function onPathFound(path) { - expect(path).not.toBeNull(); - expect(path.length).toEqual(5); - expect(path[0].x).toEqual(0); - expect(path[0].y).toEqual(0); - expect(path[3].x).toEqual(3); - expect(path[3].y).toEqual(3); - done() - } + function onPathFound(path) { + expect(path).not.toBeNull(); + expect(path.length).toEqual(5); + expect(path[0].x).toEqual(0); + expect(path[0].y).toEqual(0); + expect(path[3].x).toEqual(3); + expect(path[3].y).toEqual(3); + done() + } }); it("It should fail to find a path successfully with corner cutting disabled.", function(done) { - var easyStar = new EasyStar.js(); - easyStar.enableDiagonals(); - var map = [[1,0,0,0,0], - [0,1,0,0,0], - [0,0,1,0,0], - [0,0,0,1,0], - [0,0,0,0,1]]; + var easyStar = new EasyStar.js(); + easyStar.enableDiagonals(); + var map = [[1,0,0,0,0], + [0,1,0,0,0], + [0,0,1,0,0], + [0,0,0,1,0], + [0,0,0,0,1]]; - easyStar.setGrid(map); + easyStar.setGrid(map); - easyStar.disableCornerCutting(); + easyStar.disableCornerCutting(); - easyStar.setAcceptableTiles([1]); + easyStar.setAcceptableTiles([1]); - easyStar.findPath(0,0,4,4,onPathFound); + easyStar.findPath(0,0,4,4,onPathFound); - easyStar.calculate(); + easyStar.calculate(); - function onPathFound(path) { - expect(path).toBeNull(); - done(); - } + function onPathFound(path) { + expect(path).toBeNull(); + done(); + } }); it("It should find a path successfully.", function(done) { - var easyStar = new EasyStar.js(); - var map = [[1,1,0,1,1], - [1,1,0,1,1], - [1,1,0,1,1], - [1,1,1,1,1], - [1,1,1,1,1]]; - - easyStar.setGrid(map); - - easyStar.setAcceptableTiles([1]); - - easyStar.findPath(1,2,3,2,onPathFound); - - easyStar.calculate(); - - function onPathFound(path) { - expect(path).not.toBeNull(); - expect(path.length).toEqual(5); - expect(path[0].x).toEqual(1); - expect(path[0].y).toEqual(2); - expect(path[2].x).toEqual(2); - expect(path[2].y).toEqual(3); - done(); - } + var easyStar = new EasyStar.js(); + var map = [[1,1,0,1,1], + [1,1,0,1,1], + [1,1,0,1,1], + [1,1,1,1,1], + [1,1,1,1,1]]; + + easyStar.setGrid(map); + + easyStar.setAcceptableTiles([1]); + + easyStar.findPath(1,2,3,2,onPathFound); + + easyStar.calculate(); + + function onPathFound(path) { + expect(path).not.toBeNull(); + expect(path.length).toEqual(5); + expect(path[0].x).toEqual(1); + expect(path[0].y).toEqual(2); + expect(path[2].x).toEqual(2); + expect(path[2].y).toEqual(3); + done(); + } }); it("It should be able to avoid a separate point successfully.", function(done) { - var easyStar = new EasyStar.js(); - var map = [[1,1,0,1,1], - [1,1,0,1,1], - [1,1,0,1,1], - [1,1,1,1,1], - [1,1,1,1,1]]; + var easyStar = new EasyStar.js(); + var map = [[1,1,0,1,1], + [1,1,0,1,1], + [1,1,0,1,1], + [1,1,1,1,1], + [1,1,1,1,1]]; - easyStar.setGrid(map); + easyStar.setGrid(map); - easyStar.avoidAdditionalPoint(2,3); + easyStar.avoidAdditionalPoint(2,3); - easyStar.setAcceptableTiles([1]); + easyStar.setAcceptableTiles([1]); - easyStar.findPath(1,2,3,2,onPathFound); + easyStar.findPath(1,2,3,2,onPathFound); - easyStar.calculate(); + easyStar.calculate(); - function onPathFound(path) { - expect(path).not.toBeNull(); - expect(path.length).toEqual(7); - expect(path[0].x).toEqual(1); - expect(path[0].y).toEqual(2); - expect(path[2].x).toEqual(1); - expect(path[2].y).toEqual(4); - done(); - } + function onPathFound(path) { + expect(path).not.toBeNull(); + expect(path.length).toEqual(7); + expect(path[0].x).toEqual(1); + expect(path[0].y).toEqual(2); + expect(path[2].x).toEqual(1); + expect(path[2].y).toEqual(4); + done(); + } }); it("It should work with diagonals", function(done) { - var easyStar = new EasyStar.js(); - easyStar.enableDiagonals(); - var map = [[1,1,1,1,1], - [1,1,1,1,1], - [1,1,1,1,1], - [1,1,1,1,1], - [1,1,1,1,1]]; - - easyStar.setGrid(map); - - easyStar.setAcceptableTiles([1]); - - easyStar.findPath(0,0,4,4,onPathFound); - - easyStar.calculate(); - - function onPathFound(path) { - expect(path).not.toBeNull(); - expect(path.length).toEqual(5); - expect(path[0].x).toEqual(0); - expect(path[0].y).toEqual(0); - expect(path[1].x).toEqual(1); - expect(path[1].y).toEqual(1); - expect(path[2].x).toEqual(2); - expect(path[2].y).toEqual(2); - expect(path[3].x).toEqual(3); - expect(path[3].y).toEqual(3); - expect(path[4].x).toEqual(4); - expect(path[4].y).toEqual(4); - done(); - } + var easyStar = new EasyStar.js(); + easyStar.enableDiagonals(); + var map = [[1,1,1,1,1], + [1,1,1,1,1], + [1,1,1,1,1], + [1,1,1,1,1], + [1,1,1,1,1]]; + + easyStar.setGrid(map); + + easyStar.setAcceptableTiles([1]); + + easyStar.findPath(0,0,4,4,onPathFound); + + easyStar.calculate(); + + function onPathFound(path) { + expect(path).not.toBeNull(); + expect(path.length).toEqual(5); + expect(path[0].x).toEqual(0); + expect(path[0].y).toEqual(0); + expect(path[1].x).toEqual(1); + expect(path[1].y).toEqual(1); + expect(path[2].x).toEqual(2); + expect(path[2].y).toEqual(2); + expect(path[3].x).toEqual(3); + expect(path[3].y).toEqual(3); + expect(path[4].x).toEqual(4); + expect(path[4].y).toEqual(4); + done(); + } }); it("It should move in a straight line with diagonals", function(done) { - var easyStar = new EasyStar.js(); - easyStar.enableDiagonals(); - var map = [[1,1,1,1,1,1,1,1,1,1], - [1,1,0,1,1,1,1,0,1,1], - [1,1,1,1,1,1,1,1,1,1], - [1,1,1,1,1,1,1,1,1,1], - [1,1,1,1,1,1,1,1,1,1], - [1,1,1,1,1,1,1,1,1,1], - [1,1,1,1,1,1,1,1,1,1], - [1,1,1,1,1,1,1,1,1,1], - [1,1,1,1,1,1,1,1,1,1], - [1,1,1,1,1,1,1,1,1,1]]; - - easyStar.setGrid(map); - - easyStar.enableDiagonals(); - - easyStar.setAcceptableTiles([1]); - - easyStar.findPath(0,0,9,0,onPathFound); - - easyStar.calculate(); - - function onPathFound(path) { - expect(path).not.toBeNull(); - for (var i = 0; i < path.length; i++) { - expect(path[i].y).toEqual(0); - } - done(); - } + var easyStar = new EasyStar.js(); + easyStar.enableDiagonals(); + var map = [[1,1,1,1,1,1,1,1,1,1], + [1,1,0,1,1,1,1,0,1,1], + [1,1,1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1,1,1]]; + + easyStar.setGrid(map); + + easyStar.enableDiagonals(); + + easyStar.setAcceptableTiles([1]); + + easyStar.findPath(0,0,9,0,onPathFound); + + easyStar.calculate(); + + function onPathFound(path) { + expect(path).not.toBeNull(); + for (var i = 0; i < path.length; i++) { + expect(path[i].y).toEqual(0); + } + done(); + } }); it("It should return empty path when start and end are the same tile.", function(done) { - var easyStar = new EasyStar.js(); - var map = [[1,1,0,1,1], - [1,1,0,1,1], - [1,1,0,1,1], - [1,1,1,1,1], - [1,1,1,1,1]]; + var easyStar = new EasyStar.js(); + var map = [[1,1,0,1,1], + [1,1,0,1,1], + [1,1,0,1,1], + [1,1,1,1,1], + [1,1,1,1,1]]; - easyStar.setGrid(map); + easyStar.setGrid(map); - easyStar.setAcceptableTiles([1]); + easyStar.setAcceptableTiles([1]); - easyStar.findPath(1,2,1,2,onPathFound); + easyStar.findPath(1,2,1,2,onPathFound); - easyStar.calculate(); + easyStar.calculate(); - function onPathFound(path) { - expect(path).not.toBeNull(); - expect(path.length).toEqual(0); - done(); - } + function onPathFound(path) { + expect(path).not.toBeNull(); + expect(path.length).toEqual(0); + done(); + } }); it("It should prefer straight paths when possible", function(done) { - var easyStar = new EasyStar.js(); - easyStar.setAcceptableTiles([0]); - easyStar.enableDiagonals(); - easyStar.setGrid([ - [0, 0, 0], - [0, 0, 0], - [0, 0, 0] - ]); - - easyStar.findPath(0, 1, 2, 1, function(path){ - expect(path).not.toBeNull(); - expect(path[1].x).toEqual(1); - expect(path[1].y).toEqual(1); - done(); - }); - - easyStar.calculate(); + var easyStar = new EasyStar.js(); + easyStar.setAcceptableTiles([0]); + easyStar.enableDiagonals(); + easyStar.setGrid([ + [0, 0, 0], + [0, 0, 0], + [0, 0, 0] + ]); + + easyStar.findPath(0, 1, 2, 1, function(path){ + expect(path).not.toBeNull(); + expect(path[1].x).toEqual(1); + expect(path[1].y).toEqual(1); + done(); + }); + + easyStar.calculate(); }); it("It should prefer diagonal paths when they are faster", function(done) { - var easyStar = new EasyStar.js(); - var grid = []; - for (var i = 0; i < 20; i++) { - grid[i] = []; - for (var j = 0; j < 20; j++) { - grid[i][j] = 0; - } - } - easyStar.setGrid(grid); - easyStar.setAcceptableTiles([0]); - easyStar.enableDiagonals(); - - easyStar.findPath(4, 4, 2, 2, function(path){ - expect(path).not.toBeNull(); - expect(path.length).toEqual(3); - expect(path[1].x).toEqual(3); - expect(path[1].y).toEqual(3); - done(); - }); - - easyStar.calculate(); + var easyStar = new EasyStar.js(); + var grid = []; + for (var i = 0; i < 20; i++) { + grid[i] = []; + for (var j = 0; j < 20; j++) { + grid[i][j] = 0; + } + } + easyStar.setGrid(grid); + easyStar.setAcceptableTiles([0]); + easyStar.enableDiagonals(); + + easyStar.findPath(4, 4, 2, 2, function(path){ + expect(path).not.toBeNull(); + expect(path.length).toEqual(3); + expect(path[1].x).toEqual(3); + expect(path[1].y).toEqual(3); + done(); + }); + + easyStar.calculate(); }) }); diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..28f523c --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,35 @@ +var webpack = require('webpack'); +var config = require('./package.json'); + +var minify = process.argv.indexOf('--minify') !== -1; + +var filename = 'easystar-' + config.version +if (minify) { + filename += '.min.js'; +} else { + filename += '.js'; +} + +module.exports = { + entry: './src/easystar.js', + output: { + path: './bin', + filename: filename, + libraryTarget: "var", + library: "EasyStar" + }, + module: { + loaders: [ + { test: /\.js?$/, loader: 'babel-loader'} + ] + }, + resolve: { + extensions: ['', '.js'] + }, + plugins: [ + new webpack.optimize.UglifyJsPlugin({ + include: /\.min\.js$/, + minimize: minify + }) + ] +}; From ebd13254c860566070c1b727cebb81b13225eb8e Mon Sep 17 00:00:00 2001 From: Bryce Neal Date: Tue, 17 May 2016 19:26:29 -0700 Subject: [PATCH 08/10] Bump version --- bin/{easystar-0.2.3.js => easystar-0.3.0.js} | 0 ...asystar-0.2.3.min.js => easystar-0.3.0.min.js} | 0 bower.json | 4 ++-- karma.conf.js | 2 +- package.json | 15 ++++++--------- 5 files changed, 9 insertions(+), 12 deletions(-) rename bin/{easystar-0.2.3.js => easystar-0.3.0.js} (100%) rename bin/{easystar-0.2.3.min.js => easystar-0.3.0.min.js} (100%) diff --git a/bin/easystar-0.2.3.js b/bin/easystar-0.3.0.js similarity index 100% rename from bin/easystar-0.2.3.js rename to bin/easystar-0.3.0.js diff --git a/bin/easystar-0.2.3.min.js b/bin/easystar-0.3.0.min.js similarity index 100% rename from bin/easystar-0.2.3.min.js rename to bin/easystar-0.3.0.min.js diff --git a/bower.json b/bower.json index 3916d94..7c53922 100644 --- a/bower.json +++ b/bower.json @@ -1,9 +1,9 @@ { "name": "easystarjs", - "version": "0.2.3", + "version": "0.3.0", "homepage": "https://github.com/prettymuchbryce/easystarjs", "description": "Asynchronous A* Pathfinding API", - "main": "./bin/easystar-0.2.3.min.js", + "main": "./bin/easystar-0.3.0.min.js", "keywords": [ "A*", "pathfinding", diff --git a/karma.conf.js b/karma.conf.js index 8b4762f..388059b 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -20,7 +20,7 @@ module.exports = function(config) { // list of files / patterns to load in the browser files: [ - './bin/easystar-0.2.3.js', + './bin/easystar-0.3.0.js', './test/easystartest.js' ], diff --git a/package.json b/package.json index c8123df..705d13a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "easystarjs", - "version": "0.2.3", + "version": "0.3.0", "main": "./src/easystar.js", "description": "Asynchronous A* Pathfinding API", "scripts": { @@ -25,19 +25,16 @@ "url": "http://bryce.is" }, "devDependencies": { - "gulp": "^3.8.0", - "gulp-concat": "^2.2.0", - "gulp-karma": "0.0.4", - "gulp-uglify": "^0.3.0", "karma": "^0.12.16", "karma-coverage": "^0.2.4", "karma-jasmine": "^0.3.5", - "karma-phantomjs-launcher": "^0.1.4" - }, - "dependencies": { + "karma-phantomjs-launcher": "^0.1.4", + "webpack": "^1.13.0", "babel-core": "^6.8.0", "babel-loader": "^6.2.4", - "heap": "^0.2.6", "webpack": "^1.13.0" + }, + "dependencies": { + "heap": "0.2.6" } } From 15cc5d33c2ad29e920b0629a51f429910fa20494 Mon Sep 17 00:00:00 2001 From: Bryce Neal Date: Tue, 17 May 2016 19:26:41 -0700 Subject: [PATCH 09/10] Update demo --- demo/static/js/easystar-0.1.7.min.js | 1 - demo/static/js/easystar-0.3.0.min.js | 1 + demo/views/index.html | 18 ++++-------------- 3 files changed, 5 insertions(+), 15 deletions(-) delete mode 100644 demo/static/js/easystar-0.1.7.min.js create mode 100644 demo/static/js/easystar-0.3.0.min.js diff --git a/demo/static/js/easystar-0.1.7.min.js b/demo/static/js/easystar-0.1.7.min.js deleted file mode 100644 index 304ab9c..0000000 --- a/demo/static/js/easystar-0.1.7.min.js +++ /dev/null @@ -1 +0,0 @@ -var EasyStar=EasyStar||{};"undefined"==typeof window&&"undefined"!=typeof module&&module.exports&&(module.exports=EasyStar),EasyStar.Node=function(a,b,c,d,e){this.parent=a,this.x=b,this.y=c,this.costSoFar=d,this.simpleDistanceToTarget=e,this.bestGuessDistance=function(){return this.costSoFar+this.simpleDistanceToTarget}},EasyStar.Node.OPEN_LIST=0,EasyStar.Node.CLOSED_LIST=1,EasyStar.PriorityQueue=function(a,b){this.length=0;var c=[],d=!1;if(b==EasyStar.PriorityQueue.MAX_HEAP)d=!0;else{if(b!=EasyStar.PriorityQueue.MIN_HEAP)throw b+" not supported.";d=!1}this.insert=function(b){if(!b.hasOwnProperty(a))throw"Cannot insert "+b+" because it does not have a property by the name of "+a+".";c.push(b),this.length++,e(this.length-1)},this.getHighestPriorityElement=function(){return c[0]},this.shiftHighestPriorityElement=function(){if(0===this.length)throw"There are no more elements in your priority queue.";if(1===this.length){var a=c[0];return c=[],this.length=0,a}var b=c[0],d=c.pop();return this.length--,c[0]=d,f(0),b};var e=function(a){if(0!==a){var b=i(a);h(a,b)&&(g(a,b),e(b))}},f=function(a){var b=j(a),c=k(a);if(h(b,a))g(a,b),f(b);else if(h(c,a))g(a,c),f(c);else{if(0==a)return;f(0)}},g=function(a,b){var d=c[a];c[a]=c[b],c[b]=d},h=function(b,e){if(void 0===c[e]||void 0===c[b])return!1;var f,g;return"function"==typeof c[b][a]?(f=c[b][a](),g=c[e][a]()):(f=c[b][a],g=c[e][a]),d?f>g?!0:!1:g>f?!0:!1},i=function(a){return Math.floor(a/2)-1},j=function(a){return 2*a+1},k=function(a){return 2*a+2}},EasyStar.PriorityQueue.MAX_HEAP=0,EasyStar.PriorityQueue.MIN_HEAP=1,EasyStar.instance=function(){this.isDoneCalculating=!0,this.pointsToAvoid={},this.startX,this.callback,this.startY,this.endX,this.endY,this.nodeHash={},this.openList},EasyStar.js=function(){var a,b,c,d=10,e=14,f={},g={},h=[],i=Number.MAX_VALUE,j=!1;this.setAcceptableTiles=function(a){a instanceof Array?c=a:!isNaN(parseFloat(a))&&isFinite(a)&&(c=[a])},this.enableDiagonals=function(){j=!0},this.disableDiagonals=function(){j=!1},this.setGrid=function(b){a=b;for(var c=0;cb||0>e||0>f||0>f||b>a[0].length-1||e>a.length-1||f>a[0].length-1||g>a.length-1)throw"Your start or end point is outside the scope of your grid.";b===f&&e===g&&i([]);for(var j=a[g][f],k=!1,m=0;mb;b++){if(0===h.length)return;if(0!==h[0].openList.length){var f=h[0].openList.shiftHighestPriorityElement();if(f.list=EasyStar.Node.CLOSED_LIST,f.y>0&&(k(h[0],f,0,-1,d*g[a[f.y-1][f.x]]),h[0].isDoneCalculating===!0))h.shift();else if(f.x0&&(k(h[0],f,-1,0,d*g[a[f.y][f.x-1]]),h[0].isDoneCalculating===!0))h.shift();else if(j){if(f.x>0&&f.y>0&&(k(h[0],f,-1,-1,e*g[a[f.y-1][f.x-1]]),h[0].isDoneCalculating===!0)){h.shift();continue}if(f.x0&&(k(h[0],f,1,-1,e*g[a[f.y-1][f.x+1]]),h[0].isDoneCalculating===!0)){h.shift();continue}if(f.x>0&&f.yn||0>r||0>a||0>u||n>t[0].length-1||r>t.length-1||a>t[0].length-1||u>t.length-1)throw new Error("Your start or end point is outside the scope of your grid.");if(n===a&&r===u)return void h([]);for(var p=t[u][a],f=!1,d=0;dn;n++){if(0===y.length)return;if(l&&(n=0),0!==y[0].openList.size()){var i=y[0].openList.pop();if(y[0].endX===i.x&&y[0].endY===i.y){y[0].isDoneCalculating=!0;var r=[];r.push({x:i.x,y:i.y});for(var s=i.parent;null!=s;)r.push({x:s.x,y:s.y}),s=s.parent;r.reverse();var u=y[0],h=r;return void u.callback(h)}var p=[];i.list=a,i.y>0&&p.push({instance:y[0],searchNode:i,x:0,y:-1,cost:o*m(i.x,i.y-1)}),i.x0&&p.push({instance:y[0],searchNode:i,x:-1,y:0,cost:o*m(i.x-1,i.y)}),g&&(i.x>0&&i.y>0&&(d||b(t,e,i.x,i.y-1)&&b(t,e,i.x-1,i.y))&&p.push({instance:y[0],searchNode:i,x:-1,y:-1,cost:c*m(i.x-1,i.y-1)}),i.x0&&(d||b(t,e,i.x,i.y-1)&&b(t,e,i.x+1,i.y))&&p.push({instance:y[0],searchNode:i,x:1,y:-1,cost:c*m(i.x+1,i.y-1)}),i.x>0&&i.yi?c*i+r:c*r+i}var i=Math.abs(t-e),r=Math.abs(n-o);return i+r}}},function(t,n){t.exports=function(){this.isDoneCalculating=!0,this.pointsToAvoid={},this.startX,this.callback,this.startY,this.endX,this.endY,this.nodeHash={},this.openList}},function(t,n){t.exports=function(t,n,e,o,i){this.parent=t,this.x=n,this.y=e,this.costSoFar=o,this.simpleDistanceToTarget=i,this.bestGuessDistance=function(){return this.costSoFar+this.simpleDistanceToTarget}}},function(t,n,e){t.exports=e(4)},function(t,n,e){var o,i,r;(function(){var e,s,a,u,c,l,h,p,f,d,y,v,g,x,b;a=Math.floor,d=Math.min,s=function(t,n){return n>t?-1:t>n?1:0},f=function(t,n,e,o,i){var r;if(null==e&&(e=0),null==i&&(i=s),0>e)throw new Error("lo must be non-negative");for(null==o&&(o=t.length);o>e;)r=a((e+o)/2),i(n,t[r])<0?o=r:e=r+1;return[].splice.apply(t,[e,e-e].concat(n)),n},l=function(t,n,e){return null==e&&(e=s),t.push(n),x(t,0,t.length-1,e)},c=function(t,n){var e,o;return null==n&&(n=s),e=t.pop(),t.length?(o=t[0],t[0]=e,b(t,0,n)):o=e,o},p=function(t,n,e){var o;return null==e&&(e=s),o=t[0],t[0]=n,b(t,0,e),o},h=function(t,n,e){var o;return null==e&&(e=s),t.length&&e(t[0],n)<0&&(o=[t[0],n],n=o[0],t[0]=o[1],b(t,0,e)),n},u=function(t,n){var e,o,i,r,u,c;for(null==n&&(n=s),r=function(){c=[];for(var n=0,e=a(t.length/2);e>=0?e>n:n>e;e>=0?n++:n--)c.push(n);return c}.apply(this).reverse(),u=[],o=0,i=r.length;i>o;o++)e=r[o],u.push(b(t,e,n));return u},g=function(t,n,e){var o;return null==e&&(e=s),o=t.indexOf(n),-1!==o?(x(t,0,o,e),b(t,o,e)):void 0},y=function(t,n,e){var o,i,r,a,c;if(null==e&&(e=s),i=t.slice(0,n),!i.length)return i;for(u(i,e),c=t.slice(n),r=0,a=c.length;a>r;r++)o=c[r],h(i,o,e);return i.sort(e).reverse()},v=function(t,n,e){var o,i,r,a,l,h,p,y,v,g;if(null==e&&(e=s),10*n<=t.length){if(a=t.slice(0,n).sort(e),!a.length)return a;for(r=a[a.length-1],y=t.slice(n),l=0,p=y.length;p>l;l++)o=y[l],e(o,r)<0&&(f(a,o,0,null,e),a.pop(),r=a[a.length-1]);return a}for(u(t,e),g=[],i=h=0,v=d(n,t.length);v>=0?v>h:h>v;i=v>=0?++h:--h)g.push(c(t,e));return g},x=function(t,n,e,o){var i,r,a;for(null==o&&(o=s),i=t[e];e>n&&(a=e-1>>1,r=t[a],o(i,r)<0);)t[e]=r,e=a;return t[e]=i},b=function(t,n,e){var o,i,r,a,u;for(null==e&&(e=s),i=t.length,u=n,r=t[n],o=2*n+1;i>o;)a=o+1,i>a&&!(e(t[o],t[a])<0)&&(o=a),t[n]=t[o],n=o,o=2*n+1;return t[n]=r,x(t,u,n,e)},e=function(){function t(t){this.cmp=null!=t?t:s,this.nodes=[]}return t.push=l,t.pop=c,t.replace=p,t.pushpop=h,t.heapify=u,t.updateItem=g,t.nlargest=y,t.nsmallest=v,t.prototype.push=function(t){return l(this.nodes,t,this.cmp)},t.prototype.pop=function(){return c(this.nodes,this.cmp)},t.prototype.peek=function(){return this.nodes[0]},t.prototype.contains=function(t){return-1!==this.nodes.indexOf(t)},t.prototype.replace=function(t){return p(this.nodes,t,this.cmp)},t.prototype.pushpop=function(t){return h(this.nodes,t,this.cmp)},t.prototype.heapify=function(){return u(this.nodes,this.cmp)},t.prototype.updateItem=function(t){return g(this.nodes,t,this.cmp)},t.prototype.clear=function(){return this.nodes=[]},t.prototype.empty=function(){return 0===this.nodes.length},t.prototype.size=function(){return this.nodes.length},t.prototype.clone=function(){var n;return n=new t,n.nodes=this.nodes.slice(0),n},t.prototype.toArray=function(){return this.nodes.slice(0)},t.prototype.insert=t.prototype.push,t.prototype.top=t.prototype.peek,t.prototype.front=t.prototype.peek,t.prototype.has=t.prototype.contains,t.prototype.copy=t.prototype.clone,t}(),function(e,s){return i=[],o=s,r="function"==typeof o?o.apply(n,i):o,!(void 0!==r&&(t.exports=r))}(this,function(){return e})}).call(this)}]); \ No newline at end of file diff --git a/demo/views/index.html b/demo/views/index.html index 8e0e4fb..a0bed58 100755 --- a/demo/views/index.html +++ b/demo/views/index.html @@ -26,12 +26,8 @@
- Iterations-Per-Calculation (asynchronicity)
+ Iterations-Per-Calculation (asynchrony)
-
-
-
-
@@ -71,7 +67,7 @@ - +