forked from Open-CT/openct-tasks
144 lines
3.1 KiB
JavaScript
144 lines
3.1 KiB
JavaScript
var DijkstraGraph = (function (undefined) {
|
|
|
|
var extractKeys = function (obj) {
|
|
var keys = [], key;
|
|
for (key in obj) {
|
|
Object.prototype.hasOwnProperty.call(obj,key) && keys.push(key);
|
|
}
|
|
return keys;
|
|
}
|
|
|
|
var sorter = function (a, b) {
|
|
return parseFloat (a) - parseFloat (b);
|
|
}
|
|
|
|
var findPaths = function (map, start, end, infinity) {
|
|
infinity = infinity || Infinity;
|
|
|
|
var costs = {},
|
|
open = {'0': [start]},
|
|
predecessors = {},
|
|
keys;
|
|
|
|
var addToOpen = function (cost, vertex) {
|
|
var key = "" + cost;
|
|
if (!open[key]) open[key] = [];
|
|
open[key].push(vertex);
|
|
}
|
|
|
|
costs[start] = 0;
|
|
|
|
while (open) {
|
|
if(!(keys = extractKeys(open)).length) break;
|
|
|
|
keys.sort(sorter);
|
|
|
|
var key = keys[0],
|
|
bucket = open[key],
|
|
node = bucket.shift(),
|
|
currentCost = parseFloat(key),
|
|
adjacentNodes = map[node] || {};
|
|
|
|
if (!bucket.length) delete open[key];
|
|
|
|
for (var vertex in adjacentNodes) {
|
|
if (Object.prototype.hasOwnProperty.call(adjacentNodes, vertex)) {
|
|
var cost = adjacentNodes[vertex],
|
|
totalCost = cost + currentCost,
|
|
vertexCost = costs[vertex];
|
|
|
|
if ((vertexCost === undefined) || (vertexCost > totalCost)) {
|
|
costs[vertex] = totalCost;
|
|
addToOpen(totalCost, vertex);
|
|
predecessors[vertex] = node;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (costs[end] === undefined) {
|
|
return null;
|
|
} else {
|
|
return predecessors;
|
|
}
|
|
|
|
}
|
|
|
|
var extractShortest = function (predecessors, end) {
|
|
var nodes = [],
|
|
u = end;
|
|
|
|
while (u !== undefined) {
|
|
nodes.push(u);
|
|
u = predecessors[u];
|
|
}
|
|
|
|
nodes.reverse();
|
|
return nodes;
|
|
}
|
|
|
|
var findShortestPath = function (map, nodes) {
|
|
var start = nodes.shift(),
|
|
end,
|
|
predecessors,
|
|
path = [],
|
|
shortest;
|
|
|
|
while (nodes.length) {
|
|
end = nodes.shift();
|
|
predecessors = findPaths(map, start, end);
|
|
|
|
if (predecessors) {
|
|
shortest = extractShortest(predecessors, end);
|
|
if (nodes.length) {
|
|
path.push.apply(path, shortest.slice(0, -1));
|
|
} else {
|
|
return path.concat(shortest);
|
|
}
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
start = end;
|
|
}
|
|
}
|
|
|
|
var toArray = function (list, offset) {
|
|
try {
|
|
return Array.prototype.slice.call(list, offset);
|
|
} catch (e) {
|
|
var a = [];
|
|
for (var i = offset || 0, l = list.length; i < l; ++i) {
|
|
a.push(list[i]);
|
|
}
|
|
return a;
|
|
}
|
|
}
|
|
|
|
var DijkstraGraph = function (map) {
|
|
this.map = map;
|
|
}
|
|
|
|
DijkstraGraph.prototype.findShortestPath = function (start, end) {
|
|
if (Object.prototype.toString.call(start) === '[object Array]') {
|
|
return findShortestPath(this.map, start);
|
|
} else if (arguments.length === 2) {
|
|
return findShortestPath(this.map, [start, end]);
|
|
} else {
|
|
return findShortestPath(this.map, toArray(arguments));
|
|
}
|
|
}
|
|
|
|
DijkstraGraph.findShortestPath = function (map, start, end) {
|
|
if (Object.prototype.toString.call(start) === '[object Array]') {
|
|
return findShortestPath(map, start);
|
|
} else if (arguments.length === 3) {
|
|
return findShortestPath(map, [start, end]);
|
|
} else {
|
|
return findShortestPath(map, toArray(arguments, 1));
|
|
}
|
|
}
|
|
|
|
return DijkstraGraph;
|
|
|
|
})(); |