(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);