/// <summary>
        ///  Retrieves the path distance from one node to another, or calculates
        ///  it if it has not yet been found and CalculateFully has not been called.
        /// </summary>
        /// <param name="a">The first graph node.</param>
        /// <param name="b">The second graph node.</param>
        /// <returns>The length of the path from a to b (equals the amount of edges
        /// traversed).</returns>
        /// <remarks>
        ///  If CalculateFully has been called and the nodes are not connected, 0 will be returned.
        ///  If CalculateFully has been called and the nodes were not both passed to it, a IndexOutOfRangeException will be thrown.
        ///  If CalculateFully has not been called and the nodes are not connected, a GraphNotConnectedException will be thrown.
        /// </remarks>
        public int this[GraphNode a, GraphNode b]
        {
            get
            {
                if (FullyCached)
                {
                    return _distancesFast[a.DistancesIndex, b.DistancesIndex];
                }

                var index = GetIndex(a, b);
                if (!_distances.ContainsKey(index))
                {
                    Dijkstra(a, b);
                }
                return _distances[index];
            }
        }
示例#2
0
 public GraphEdge(GraphNode inside, GraphNode outside)
 {
     Inside = inside;
     Outside = outside;
 }
        /// <summary>
        /// Adds the distance and shortest path between from and to to the respectives
        /// dictionarys if not already present.
        /// </summary>
        private void AddEdge(GraphNode from, GraphNode to, int distFromStart, IDictionary<ushort, ushort> predecessors)
        {
            var length = distFromStart + 1;

            if (FullyCached)
            {
                var i1 = from.DistancesIndex;
                var i2 = to.DistancesIndex;
                if (_pathsFast[i1, i2] != null) return;

                var path = length > 0 ? GenerateShortestPath(from.Id, to.Id, predecessors, length) : new ushort[0];
                _distancesFast[i1, i2] = _distancesFast[i2, i1] = length;
                _pathsFast[i1, i2] = _pathsFast[i2, i1] = path;
            }
            else
            {
                var index = GetIndex(from, to);
                if (_distances.ContainsKey(index)) return;

                var path = length > 0 ? GenerateShortestPath(from.Id, to.Id, predecessors, length) : new ushort[0];
                _paths[index] = path;
                _distances[index] = length;
            }
        }
        /// <summary>
        ///  Uses a djikstra-like algorithm to flood the graph from the start
        ///  node until the target node is found (if specified) or until all marked nodes got checked.
        /// </summary>
        /// <param name="start">The starting node. (not null)</param>
        /// <param name="target">The (optional) target node.</param>
        /// <exception cref="GraphNotConnectedException">
        /// If target node is not null and it could not be found.
        /// </exception>
        private void Dijkstra(GraphNode start, GraphNode target = null)
        {
            if (start == null) throw new ArgumentNullException("start");

            AddEdge(start, start, -1, null);
            if (start == target) return;

            // The last newly found nodes.
            var front = new HashSet<GraphNode>() { start };
            // The already visited nodes.
            var visited = new HashSet<GraphNode>() { start };
            // The dictionary of the predecessors of the visited nodes.
            var predecessors = new Dictionary<ushort, ushort>();
            // The traversed distance from the starting node in edges.
            var distFromStart = 0;

            while (front.Count > 0)
            {
                var newFront = new HashSet<GraphNode>();

                foreach (var node in front)
                {
                    foreach (var adjacentNode in node.Adjacent)
                    {
                        if (visited.Contains(adjacentNode))
                            continue;

                        predecessors[adjacentNode.Id] = node.Id;

                        if (adjacentNode == target)
                        {
                            AddEdge(start, adjacentNode, distFromStart, predecessors);
                            return;
                        }
                        if (adjacentNode.DistancesIndex >= 0)
                        {
                            AddEdge(start, adjacentNode, distFromStart, predecessors);
                        }

                        newFront.Add(adjacentNode);
                        visited.Add(adjacentNode);
                    }
                }

                front = newFront;
                distFromStart++;
            }

            // Target node was not found because start and target are not connected.
            if (target != null)
                throw new GraphNotConnectedException();
        }
 /// <summary>
 ///  Compounds two ushort node indices into a single uint one, which
 ///  is independent of the order of the two indices.
 /// </summary>
 /// <param name="a">The first index.</param>
 /// <param name="b">The second index.</param>
 /// <returns>The compounded index.</returns>
 private static uint GetIndex(GraphNode a, GraphNode b)
 {
     var aId = a.Id;
     var bId = b.Id;
     return (uint)(Math.Min(aId, bId) << 16) + Math.Max(aId, bId);
 }
 /// <summary>
 /// Returns whether the given nodes are connected.
 /// </summary>
 public bool AreConnected(GraphNode a, GraphNode b)
 {
     try
     {
         // Null if not connected and _fullyCached
         // Exception if not connected and not _fullyCached
         return GetShortestPath(a, b) != null;
     }
     catch (GraphNotConnectedException)
     {
         return false;
     }
 }
        /// <summary>
        ///  Retrieves the shortest path from one node to another, or calculates
        ///  it if it has not yet been found and CalculateFully has not been called.
        /// </summary>
        /// <param name="a">The first graph node. (not null)</param>
        /// <param name="b">The second graph node. (not null)</param>
        /// <returns>The shortest path from a to b, not containing either and ordered from a to b or b to a.</returns>
        /// <remarks>
        ///  If CalculateFully has been called and the nodes are not connected, null will be returned.
        ///  If CalculateFully has been called and the nodes were not both passed to it, a IndexOutOfRangeException will be thrown.
        ///  If CalculateFully has not been called and the nodes are not connected, a GraphNotConnectedException will be thrown.
        /// </remarks>
        public ushort[] GetShortestPath(GraphNode a, GraphNode b)
        {
            if (FullyCached)
            {
                return _pathsFast[a.DistancesIndex, b.DistancesIndex];
            }

            var index = GetIndex(a, b);
            if (!_distances.ContainsKey(index))
            {
                Dijkstra(a, b);
            }
            return _paths[index];
        }