Exemple #1
0
        /// <summary>
        /// Finds path between From and To points
        /// </summary>
        /// <param name="from">The start point</param>
        /// <param name="to">The destination point</param>
        /// <param name="length">Length of the path in meters</param>
        /// <returns>Path as list of PathSegments beteewn two point. If path wasn't found returns null.</returns>
        public IList<PathSegment> FindPath(CandidatePoint from, CandidatePoint to, ref double length)
        {
            Initialize(from, to);

            while (_open.Count > 0) {
                PartialPath current = _open.RemoveTop();
                _close.Add(current.End, current);

                // Path found
                if(current.End.MapPoint == to.MapPoint) {
                    length = current.Length;
                    var result = BuildPath(current, from);
                    Finalize(to);

                    return result;
                }

                foreach (var link in current.End.Connections) {
                    if (link.From != current.End) continue;

                    double distance;
                    if (link.To.MapPoint == to.MapPoint)
                        distance = current.Length + Calculations.GetPathLength(current.End.MapPoint, to.MapPoint, link.Geometry);
                    else
                        distance = current.Length + link.Geometry.Length;

                    if (_open.Contains(link.To)) {
                        // Update previously found path in the open list (if this one is shorter)
                        PartialPath p = _open[link.To];
                        if (p.Length > distance) {
                            p.PreviousNode = current.End;
                            p.PathFromPrevious = link.Geometry;
                            _open.Update(p, distance);
                        }
                    }
                    else if (_close.ContainsKey(link.To)) {
                        // Update previously found path in the close list (if this one is shorter)
                        if (_close[link.To].Length > distance) {
                            _close[link.To].Length = distance;
                            _close[link.To].End = current.End;
                            _close[link.To].PathFromPrevious = link.Geometry;
                        }
                    }
                    else {
                        // Expand path to new node
                        PartialPath expanded = new PartialPath() {
                            Length = distance,
                            EstimationToEnd = Calculations.GetDistance2D(link.To.MapPoint, to.MapPoint),
                                                                     End = link.To, PreviousNode = current.End, PathFromPrevious = link.Geometry };
                        _open.Add(expanded);
                    }
                }
            }

            Finalize(to);
            return null;
        }
Exemple #2
0
        /// <summary>
        /// Builds result path
        /// </summary>
        /// <param name="lastPathPart"></param>
        /// <param name="from"></param>
        /// <returns></returns>
        IList<PathSegment> BuildPath(PartialPath lastPathPart, CandidatePoint from)
        {
            List<PathSegment> result = new List<PathSegment>();

            while (lastPathPart.PreviousNode != null) {
                result.Add(new PathSegment() { From = lastPathPart.PreviousNode, To = lastPathPart.End, Road = lastPathPart.PathFromPrevious });
                lastPathPart = _close[lastPathPart.PreviousNode];
            }

            result.Add(new PathSegment() { From = new Node(from.MapPoint), To = lastPathPart.End, Road = lastPathPart.PathFromPrevious });
            result.Reverse();

            return result;
        }
Exemple #3
0
        /// <summary>
        /// Initializes internal properties before search
        /// </summary>
        /// <param name="from">The start point</param>
        /// <param name="to">The destination point</param>
        void Initialize(CandidatePoint from, CandidatePoint to)
        {
            _open.Clear();
            _close.Clear();

            // Add nodes reachable from the From point to the open list
            foreach (var connection in from.Road.Connections) {
                PartialPath path = new PartialPath() {End = connection.To, PathFromPrevious = connection.Geometry,
                                                                                            Length = Calculations.GetPathLength(from.MapPoint, connection.To.MapPoint, connection.Geometry),
                                                                                            EstimationToEnd = Calculations.GetDistance2D(connection.To.MapPoint, to.MapPoint)
                };

                if (_open.Contains(path)) {
                    if (_open[path.End].Length > path.Length) {
                        _open.Remove(_open[path.End]);
                        _open.Add(path);
                    }
                }
                else {
                    _open.Add(path);
                }
            }

            _temporaryConnections.Clear();
            // Add temporary connections to the TO point
            foreach (var targetConnections in to.Road.Connections) {
                Connection destinationConnection = new Connection(targetConnections.From, new Node(to.MapPoint)) { Geometry = to.Road };
                _temporaryConnections.Add(destinationConnection);
            }
        }
Exemple #4
0
        /// <inheritdoc />
        public Path <GraphNode> FindPath(
            GraphNode sourceNode,
            GraphNode targetNode,
            IEqualityComparer <GraphNode> targetEqualityComparer,
            IEqualityComparer <GraphNode> nodeEqualityComparer
            )
        {
            // If there is a limit on search time, we need to initialize the timer
            var timer = new StopwatchExecutionTimer();

            // Set of already visited nodes
            var closed = new HashSet <GraphNode>(nodeEqualityComparer);
            // Priority queue of partial and potentially complete paths
            var open = new ListBasedPriorityQueue <PartialPath>();

            open.Add(new PartialPath(this.heuristic(sourceNode, targetNode)));

            // Best known path to target in the open priority queue
            // (Used as a fallback, if time limit is exceeded before the search ends)
            PartialPath bestPathToTarget = null;

            // Cost of the cheapest known path to target
            // (Used to discard costlier paths, if the assumption of non-negative edge costs is in effect)
//			double minTotalCost = double.PositiveInfinity;

            // While there are unexplored nodes
            while (open.Count > 0)
            {
                // Pop partial path with the lowest estimated cost from the priority queue
                var currentPartialPath = open.PopFront();
                // Node to explore
                GraphNode currentNode;

                // If partial path is empty (and therefore has no last node to speak of),
                // it means we are at the source of our search.
                if (currentPartialPath.IsEmpty)
                {
                    // Explore the source node
                    currentNode = sourceNode;
                }
                else
                {
                    // Explore the last node of the current partial path
                    currentNode = currentPartialPath.LastNode;
                }

                // If the currently examined node has been examined previously, don't consider it further.
                // This has the effect of discarding the partial path, which led to it, from the open priority queue.
                // FIXME: As far as I understand, this is correct behaviour only if the heuristic is admissable!
                // In general case (i.e. with inadmissable heuristics), closed nodes may need to be reopened.
                // To control this behaviour an additional flag may be added to the constructor and checked here.
                if (closed.Contains(currentNode))
                {
                    continue;
                }
                // Will only be called if ASTAR_VALIDATE_NODE_HASH_IMPLEMENTATION is defined
                ValidateNodeHashImplementation(closed, currentNode);

                // If the currently examined node is the target
                if (targetEqualityComparer.Equals(currentNode, targetNode))
                {
                    // Hurray!
                    return(currentPartialPath.ToPath());
                }

                // If there is a time limit, check the timer.
                // If the time limit has been exceeded, return the best known path to target or,
                // if there has been no such path discovered, break and report a failure.
                if (this.maxSecondsPerSearch < float.PositiveInfinity && timer.ElapsedSeconds > this.maxSecondsPerSearch)
                {
                    if (bestPathToTarget != null)
                    {
                        // "Good enough"
                        return(bestPathToTarget.ToPath());
                    }
                    else
                    {
                        throw new PathfindingTimeoutException(this.GetType(), this.maxSecondsPerSearch);
                    }
                }

                // If we have a limit on max search depth, and current path is at max depth limit,
                // don't add paths to currentNode's neighbours to the open priority queue,
                // since those paths will exceed the max search depth, and don't place currentNode
                // into closed set, since there might be a more expensive path with smaller edge count,
                // which leads to it without exceeding the max seatch depth limit.
                if (this.maxSearchDepth >= 0 && currentPartialPath.EdgeCount >= this.maxSearchDepth)
                {
                    continue;
                }

                // FIXME: Remove the commented out code.
                // The following check makes no sense: priority queue already guarantees that
                // the current path is at most as expensive as the best known path to target.
                // Maybe if it checked for >=, instead of >, i.e. assumed positive and not merely non-negative costs,
                // it would have miniscule effect, but even so the benefits would likely be negligible.

                // If the assumption of non-negative costs is in effect, we can discard the current path
                // from further consideration, if it's already costlier than the cheapest known path
                // that reaches the target node, because if all the edges have non-negative cost,
                // we can be certain that it will not get any cheaper.
                // (At this point we know that the current path does not reach the target,
                // otherwise we would've already returned it earlier in this loop iteration.)
//				if(this.assumeNonNegativeCosts && currentPartialPath.CostSoFar > minTotalCost)
//				{
//					continue;
//				}

                // Mark currentNode as visited by placing it into closed set.
                closed.Add(currentNode);
                // Insert paths from sourceNode to currentNode's neighbours into the open priority queue
                foreach (var outgoingEdge in currentNode.OutgoingEdges)
                {
                    var neighbour       = outgoingEdge.TargetNode;
                    var pathToNeighbour = new PartialPath(currentPartialPath);
                    pathToNeighbour.AppendEdge(outgoingEdge, this.heuristic(neighbour, targetNode));
                    open.Add(pathToNeighbour);

                    // If there is a time limit, check if the neighbour is the target node
                    // and update bestPathToTarget, if needed.
                    // This allows us to keep track of the best path to target, found so far.
                    if (this.maxSecondsPerSearch < float.PositiveInfinity &&
                        targetEqualityComparer.Equals(neighbour, targetNode)
                        // Theoretically, EstimatedTotalCost could be used here, instead of CostSoFar,
                        // since heuristic SHOULD return 0 for target node.
                        // However, this behaviour is not enforced, and so CostSoFar is used for reliability.
                        && (bestPathToTarget == null || pathToNeighbour.CostSoFar < bestPathToTarget.CostSoFar))
                    {
                        bestPathToTarget = pathToNeighbour;
                    }

                    // If the assumption of non-negative costs is in effect, check if the neighbour
                    // is the target node and update minTotalCost, if needed.
//					if(this.assumeNonNegativeCosts && targetEqualityComparer.Equals(neighbour, targetNode))
//					{
//						// Theoretically, EstimatedTotalCost could be used here, instead of CostSoFar,
//						// since heuristic SHOULD return 0 for target node.
//						// However, this behaviour is not enforced, and so CostSoFar is used for reliability.
//						minTotalCost = Math.Min(minTotalCost, pathToNeighbour.CostSoFar);
//					}
                }
            }

            // Found no path to targetNode
            throw new NoPathExistsException(
                      this.GetType(),
                      this.maxSearchDepth != AstarPathfinderConfiguration <GraphNode> .UNLIMITED_SEARCH_DEPTH
                                        ? (int?)this.maxSearchDepth
                                        : (int?)null
                      );
        }
Exemple #5
0
        public Path <TGraphNode> FindPath(
            TGraphNode sourceNode,
            TGraphNode targetNode,
            IEqualityComparer <TGraphNode> targetEqualityComparer,
            IEqualityComparer <TGraphNode> nodeEqualityComparer
            )
        {
            // If there is a limit on search time, we need to initialize the timer
            var timer = new StopwatchExecutionTimer();

            // Set of already visited nodes
            var closed = new HashSet <TGraphNode>(nodeEqualityComparer);
            // Priority queue of partial and potentially complete paths
            var open = new SmartPriorityQueue <PartialPath, double> {
                new PartialPath(heuristic(sourceNode, targetNode))
            };

            // Best known path to target in the open priority queue
            PartialPath bestPathToTarget = null;
            // Cost of the cheapest known path to target
            var minTotalCost = double.PositiveInfinity;

            // While there are unexplored nodes
            while (open.Count > 0)
            {
                // Pop partial path with the lowest estimated cost from the priority queue
                var currentPartialPath = open.PopFront();

                // Node to explore
                // If partial path is empty, we are at the source of our search.
                var currentNode = currentPartialPath.IsEmpty ? sourceNode : currentPartialPath.LastNode;
                if (closed.Contains(currentNode))
                {
                    continue;
                }

                if (targetEqualityComparer.Equals(currentNode, targetNode))
                {
                    UnityEngine.Debug.Log(
                        $"Path found of cost {currentPartialPath.CostSoFar} : {currentPartialPath.EdgeCount} edges; {closed.Count} nodes expanded"
                        );
                    return(currentPartialPath.ToPath());
                }

                // If there is a time limit, check the timer.
                // If the time limit has been exceeded, return the best known path to target or,
                // otherwise break and report a failure.
                if (maxSecondsPerSearch < float.PositiveInfinity && timer.ElapsedSeconds > maxSecondsPerSearch)
                {
                    if (bestPathToTarget != null)
                    {
                        // Time out, best path so far
                        UnityEngine.Debug.Log(
                            $"Time out! Path found of cost {currentPartialPath.CostSoFar} : {currentPartialPath.EdgeCount} edges"
                            );
                        return(bestPathToTarget.ToPath());
                    }

                    throw new PathfindingTimeoutException(GetType(), maxSecondsPerSearch);
                }

                // If we have a limit on max search depth, and current path is at max depth limit,
                // don't add paths to currentNode's neighbours to the open priority queue
                if (maxSearchDepth >= 0 && currentPartialPath.EdgeCount >= maxSearchDepth)
                {
                    continue;
                }
                if (assumeNonNegativeCosts && currentPartialPath.CostSoFar > minTotalCost)
                {
                    continue;
                }

                // Mark currentNode as visited by placing it into closed set.
                closed.Add(currentNode);
                // Insert paths from sourceNode to currentNode's neighbours into the open priority queue
                foreach (var outgoingEdge in currentNode.OutgoingEdges)
                {
                    var neighbour       = outgoingEdge.TargetNode;
                    var pathToNeighbour = new PartialPath(currentPartialPath);
                    pathToNeighbour.AppendEdge(outgoingEdge, heuristic(neighbour, targetNode));
                    open.Add(pathToNeighbour);

                    // If there is a time limit, check if the neighbour is the target node
                    // and update bestPathToTarget, if needed.
                    // This allows us to keep track of the best path to target, found so far.
                    if (maxSecondsPerSearch < float.PositiveInfinity &&
                        targetEqualityComparer.Equals(neighbour, targetNode) &&
                        (bestPathToTarget == null ||
                         pathToNeighbour.EstimatedTotalCost < bestPathToTarget.CostSoFar))
                    {
                        bestPathToTarget = pathToNeighbour;
                    }

                    // If the assumption of non-negative costs is in effect, check if the neighbour
                    // is the target node and update minTotalCost, if needed.
                    if (assumeNonNegativeCosts && targetEqualityComparer.Equals(neighbour, targetNode))
                    {
                        minTotalCost = Math.Min(minTotalCost, pathToNeighbour.CostSoFar);
                    }
                }
            }

            UnityEngine.Debug.Log("No plan found");
            throw new NoPathExistsException(
                      GetType(),
                      maxSearchDepth
                      );
        }
Exemple #6
0
 public void Pattern()
 {
     var partialPath = new PartialPath(@"di");
     Assert.AreEqual("di*", partialPath.Pattern);
 }
Exemple #7
0
 public void PatternPathDeep()
 {
     var partialPath = new PartialPath(@"dir1\subdir");
     Assert.AreEqual(@"dir1\subdir*", partialPath.Pattern);
 }
Exemple #8
0
 public void NoQuotesBase()
 {
     var partialPath = new PartialPath(@"'T:\src'\dotne");
     Assert.AreEqual(@"T:\src\", partialPath.Base);
 }
Exemple #9
0
 public void NoQuotesPattern()
 {
     var partialPath = new PartialPath(@"'T:\src'\dotne");
     Assert.AreEqual(@"T:\src\dotne*", partialPath.Pattern);
 }
Exemple #10
0
 public void CompletionTargetIsRawTextEvenWhenQuoted()
 {
     var partialPath = new PartialPath(@"'T:\src'\dotne");
     Assert.AreEqual(@"'T:\src'\dotne", partialPath.CompletionTarget);
 }
Exemple #11
0
 public void CompletionTargetIsRawText()
 {
     var partialPath = new PartialPath(@"T:\src\dotne");
     Assert.AreEqual(@"T:\src\dotne", partialPath.CompletionTarget);
 }
Exemple #12
0
 public void CompletionTargetIsEqualToTextIfNoQuotes()
 {
     var partialPath = new PartialPath(@"T:\src");
     Assert.AreEqual(partialPath.Text, partialPath.CompletionTarget);
 }
Exemple #13
0
 public void BasePathTwoDeep()
 {
     var partialPath = new PartialPath(@"dir1\subdir1\Tes");
     Assert.AreEqual(@"dir1\subdir1\", partialPath.Base);
 }
Exemple #14
0
 public void BasePathNone()
 {
     var partialPath = new PartialPath(@"di");
     Assert.AreEqual(String.Empty, partialPath.Base);
 }