(function() {
|
|
'use strict';
|
|
|
|
if (typeof sigma === 'undefined') {
|
|
throw 'sigma is not declared';
|
|
}
|
|
|
|
// Default function to compute path length between two nodes:
|
|
// the euclidian
|
|
var defaultPathLengthFunction = function(node1, node2, previousPathLength) {
|
|
var isEverythingDefined =
|
|
node1 != undefined &&
|
|
node2 != undefined &&
|
|
node1.x != undefined &&
|
|
node1.y != undefined &&
|
|
node2.x != undefined &&
|
|
node2.y != undefined;
|
|
if(!isEverythingDefined) {
|
|
return undefined;
|
|
}
|
|
|
|
return (previousPathLength || 0) + Math.sqrt(
|
|
Math.pow((node2.y - node1.y), 2) + Math.pow((node2.x - node1.x), 2)
|
|
);
|
|
};
|
|
|
|
sigma.classes.graph.addMethod(
|
|
'astar',
|
|
function(srcId, destId, settings) {
|
|
var currentSettings = new sigma.classes.configurable(
|
|
// Default settings
|
|
{
|
|
// Graph is directed, affects which edges are taken into account
|
|
undirected: false,
|
|
// Function to compute the distance between two connected node
|
|
pathLengthFunction: defaultPathLengthFunction,
|
|
// Function to compute an distance between two nodes
|
|
// if undefined, uses pathLengthFunction
|
|
heuristicLengthFunction: undefined
|
|
},
|
|
settings || {});
|
|
|
|
var pathLengthFunction = currentSettings("pathLengthFunction"),
|
|
heuristicLengthFunction = currentSettings("heuristicLengthFunction") || pathLengthFunction;
|
|
|
|
var srcNode = this.nodes(srcId),
|
|
destNode = this.nodes(destId);
|
|
|
|
var closedList = {},
|
|
openList = [];
|
|
|
|
var addToLists = function(node, previousNode, pathLength, heuristicLength) {
|
|
var nodeId = node.id;
|
|
var newItem = {
|
|
pathLength: pathLength,
|
|
heuristicLength: heuristicLength,
|
|
node: node,
|
|
nodeId: nodeId,
|
|
previousNode: previousNode
|
|
};
|
|
|
|
if(closedList[nodeId] == undefined || closedList[nodeId].pathLength > pathLength) {
|
|
closedList[nodeId] = newItem;
|
|
|
|
var item;
|
|
var i;
|
|
for(i = 0; i < openList.length; i++) {
|
|
item = openList[i];
|
|
if(item.heuristicLength > heuristicLength) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
openList.splice(i, 0, newItem);
|
|
}
|
|
};
|
|
|
|
addToLists(srcNode, null, 0, 0);
|
|
|
|
var pathFound = false;
|
|
|
|
// Depending of the type of graph (directed or not),
|
|
// the neighbors are either the out neighbors or all.
|
|
var allNeighbors;
|
|
if(currentSettings("undirected")) {
|
|
allNeighbors = this.allNeighborsIndex;
|
|
}
|
|
else {
|
|
allNeighbors = this.outNeighborsIndex;
|
|
}
|
|
|
|
|
|
var inspectedItem,
|
|
neighbors,
|
|
neighbor,
|
|
pathLength,
|
|
heuristicLength,
|
|
i;
|
|
while(openList.length > 0) {
|
|
inspectedItem = openList.shift();
|
|
|
|
// We reached the destination node
|
|
if(inspectedItem.nodeId == destId) {
|
|
pathFound = true;
|
|
break;
|
|
}
|
|
|
|
neighbors = Object.keys(allNeighbors[inspectedItem.nodeId]);
|
|
for(i = 0; i < neighbors.length; i++) {
|
|
neighbor = this.nodes(neighbors[i]);
|
|
pathLength = pathLengthFunction(inspectedItem.node, neighbor, inspectedItem.pathLength);
|
|
heuristicLength = heuristicLengthFunction(neighbor, destNode);
|
|
addToLists(neighbor, inspectedItem.node, pathLength, heuristicLength);
|
|
}
|
|
}
|
|
|
|
if(pathFound) {
|
|
// Rebuilding path
|
|
var path = [],
|
|
currentNode = destNode;
|
|
|
|
while(currentNode) {
|
|
path.unshift(currentNode);
|
|
currentNode = closedList[currentNode.id].previousNode;
|
|
}
|
|
|
|
return path;
|
|
}
|
|
else {
|
|
return undefined;
|
|
}
|
|
}
|
|
);
|
|
}).call(window);
|