/// <summary> /// Search forward from one vertex. /// </summary> /// <returns></returns> private void SearchForward(BinaryHeap <EdgePath <T> > queue, EdgePath <T> current) { if (current != null) { // there is a next vertex found. // check restrictions. if (_restrictions != null && _restrictions.Update(current.Vertex) && _restrictions.Restricts(current.Vertex)) { // vertex is restricted, don't settle. return; } // get the edge enumerator. var edgeEnumerator = _graph.GetEdgeEnumerator(); // add to the settled vertices. EdgePath <T> previousLinkedRoute; if (_forwardVisits.TryGetValue(current.Vertex, out previousLinkedRoute)) { if (_weightHandler.IsLargerThan(previousLinkedRoute.Weight, current.Weight)) { // settle the vertex again if it has a better weight. _forwardVisits[current.Vertex] = current; } } else { // settled the vertex. _forwardVisits.Add(current.Vertex, current); } // get neighbours. edgeEnumerator.MoveTo(current.Vertex); // add the neighbours to the queue. while (edgeEnumerator.MoveNext()) { bool?neighbourDirection; var neighbourWeight = _weightHandler.GetEdgeWeight(edgeEnumerator.Current, out neighbourDirection); if (neighbourDirection == null || neighbourDirection.Value) { // the edge is forward, and is to higher or was not contracted at all. var neighbourNeighbour = edgeEnumerator.Neighbour; if (!_forwardVisits.ContainsKey(neighbourNeighbour)) { // if not yet settled. var routeToNeighbour = new EdgePath <T>( neighbourNeighbour, _weightHandler.Add(current.Weight, neighbourWeight), current); queue.Push(routeToNeighbour, _weightHandler.GetMetric(routeToNeighbour.Weight)); } } } } }
/// <summary> /// Contracts the given vertex. /// </summary> private void Contract(uint vertex) { // get and keep edges. var edges = new List <MetaEdge>(_graph.GetEdgeEnumerator(vertex)); // remove 'downward' edge to vertex. var i = 0; while (i < edges.Count) { _graph.RemoveEdge(edges[i].Neighbour, vertex); if (_contractedFlags[edges[i].Neighbour]) { // neighbour was already contracted, remove 'downward' edge and exclude it. _graph.RemoveEdge(vertex, edges[i].Neighbour); edges.RemoveAt(i); } else { // move to next edge. i++; } } // check for a restriction, if vertex is restricted don't add shortcuts. if (_restrictions != null && _restrictions.Update(vertex)) { if (_restrictions.Restricts(vertex)) { return; } } // loop over all edge-pairs once. for (var j = 1; j < edges.Count; j++) { var edge1 = edges[j]; bool?edge1Direction; var edge1Weight = _weightHandler.GetEdgeWeight(edge1, out edge1Direction); var edge1CanMoveForward = edge1Direction == null || edge1Direction.Value; var edge1CanMoveBackward = edge1Direction == null || !edge1Direction.Value; // figure out what witness paths to calculate. var forwardWitnesses = new bool[j]; var backwardWitnesses = new bool[j]; var targets = new List <uint>(j); var targetWeights = new List <T>(j); var targetMetrics = new List <float>(j); for (var k = 0; k < j; k++) { var edge2 = edges[k]; bool?edge2Direction; var edge2Weight = _weightHandler.GetEdgeWeight(edge2, out edge2Direction); var edge2CanMoveForward = edge2Direction == null || edge2Direction.Value; var edge2CanMoveBackward = edge2Direction == null || !edge2Direction.Value; // use witness flags to represent impossible routes. forwardWitnesses[k] = !(edge1CanMoveBackward && edge2CanMoveForward); backwardWitnesses[k] = !(edge1CanMoveForward && edge2CanMoveBackward); targets.Add(edge2.Neighbour); var totalWeight = _weightHandler.Add(edge1Weight, edge2Weight); targetWeights.Add(totalWeight); targetMetrics.Add(_weightHandler.GetMetric(totalWeight)); } // calculate all witness paths. _witnessCalculator.Calculate(_graph.Graph, edge1.Neighbour, targets, targetMetrics, ref forwardWitnesses, ref backwardWitnesses, vertex); // add contracted edges if needed. for (var k = 0; k < j; k++) { var edge2 = edges[k]; if (edge1.Neighbour == edge2.Neighbour) { // do not try to add a shortcut between identical vertices. continue; } if (!forwardWitnesses[k] && !backwardWitnesses[k]) { // add bidirectional edge. _weightHandler.AddOrUpdateEdge(_graph, edge1.Neighbour, edge2.Neighbour, vertex, null, targetWeights[k]); //_graph.AddOrUpdateEdge(edge1.Neighbour, edge2.Neighbour, // targetWeights[k], null, vertex); _weightHandler.AddOrUpdateEdge(_graph, edge2.Neighbour, edge1.Neighbour, vertex, null, targetWeights[k]); //_graph.AddOrUpdateEdge(edge2.Neighbour, edge1.Neighbour, // targetWeights[k], null, vertex); } else if (!forwardWitnesses[k]) { // add forward edge. _weightHandler.AddOrUpdateEdge(_graph, edge1.Neighbour, edge2.Neighbour, vertex, true, targetWeights[k]); //_graph.AddOrUpdateEdge(edge1.Neighbour, edge2.Neighbour, // targetWeights[k], true, vertex); _weightHandler.AddOrUpdateEdge(_graph, edge2.Neighbour, edge1.Neighbour, vertex, false, targetWeights[k]); //_graph.AddOrUpdateEdge(edge2.Neighbour, edge1.Neighbour, // targetWeights[k], false, vertex); } else if (!backwardWitnesses[k]) { // add forward edge. _weightHandler.AddOrUpdateEdge(_graph, edge1.Neighbour, edge2.Neighbour, vertex, false, targetWeights[k]); //_graph.AddOrUpdateEdge(edge1.Neighbour, edge2.Neighbour, // targetWeights[k], false, vertex); _weightHandler.AddOrUpdateEdge(_graph, edge2.Neighbour, edge1.Neighbour, vertex, true, targetWeights[k]); //_graph.AddOrUpdateEdge(edge2.Neighbour, edge1.Neighbour, // targetWeights[k], true, vertex); } } } _contractedFlags[vertex] = true; _priorityCalculator.NotifyContracted(vertex); }