Exemple #1
0
    public static Graph Defleshed(this Graph graph,
                                  Graph xray,
                                  GraphUtility.Metric metric)
    {
        Dictionary <Node, Node> xray_analogs = new Dictionary <Node, Node>();

        foreach (Node node in xray.Nodes)
        {
            xray_analogs[node] = graph.GetNearestNode(node.GetPosition());
        }

        HashSet <Edge> skeleton = new HashSet <Edge>();

        foreach (Edge bone in xray.Edges)
        {
            Path path = xray_analogs[bone.A].GetPathTo(xray_analogs[bone.B], metric);

            for (int i = 0; i < path.Count - 1; i++)
            {
                skeleton.Add(new Edge(path[i], path[i + 1]));
            }
        }

        return(GraphUtility.CreateGraph(skeleton));
    }
Exemple #2
0
    public static Graph MinimumSpanned(this Graph graph, GraphUtility.Metric metric)
    {
        if (graph.Nodes.Count() == 0)
        {
            return(new Graph());
        }

        HashSet <Node>     open_set = new HashSet <Node>(graph.WithoutHiddenNodes().Nodes);
        IEnumerable <Edge> edges    = new Graph(open_set).Edges;

        foreach (Node node in open_set)
        {
            node.ClearNeighbors();
        }

        Graph mst = new Graph();

        mst.AddNode(open_set.First());
        open_set.Remove(open_set.First());

        while (open_set.Count > 0)
        {
            Edge minimum_edge = edges
                                .Where(edge => mst.Nodes.Contains(edge.A) && open_set.Contains(edge.B))
                                .MinElement(edge => metric(edge.A, edge.B));

            minimum_edge.A.AddNeighbor(minimum_edge.B);
            minimum_edge.B.AddNeighbor(minimum_edge.A);

            open_set.Remove(minimum_edge.B);
            mst.AddNode(minimum_edge.B);
        }

        return(mst);
    }
Exemple #3
0
    public static float GetCost(this Path path,
                                GraphUtility.Metric metric,
                                Node start,
                                Node end = null)
    {
        int start_index = path.IndexOf(start);

        int end_index;

        if (end != null)
        {
            end_index = path.IndexOf(end);
        }
        else
        {
            end_index = path.Count - 1;
        }

        float cost_sum = 0;

        for (int i = start_index; i < end_index; i++)
        {
            cost_sum += metric(path[i], path[i + 1]);
        }

        return(cost_sum);
    }
Exemple #4
0
    public static Node GetNearestNode(this Path path,
                                      Vector3 position,
                                      GraphUtility.Metric metric         = null,
                                      System.Func <Node, bool> predicate = null)
    {
        if (metric == null)
        {
            metric = GraphUtility.EuclideanMetric;
        }

        if (predicate == null)
        {
            predicate = node => true;
        }

        return(path
               .Where(predicate)
               .MinElement(node => metric(node, GraphUtility.CreatePositionNode(position))));
    }
    public HighwaySystem(Terrain terrain, IEnumerable <Vector3> cities)
    {
        Vector3 terrain_size   = terrain.terrainData.size;
        Vector3 terrain_center = terrain.GetPosition() + terrain_size / 2;

        Graph terrain_graph = GraphUtility.CreateGrid(terrain, TerrainGridResolution);
        Graph city_graph    = GraphUtility.CreateHairball(cities).MinimumSpanned_Obstacle();


        //Select capital
        Node capital = city_graph.Nodes
                       .Where(node => node.Neighbors.Count() == 1)
                       .RandomElement();

        //Get edges leaving the capital
        HashSet <Edge> left_edges = new HashSet <Edge>();

        foreach (Node node in city_graph.Nodes)
        {
            if (node != capital)
            {
                left_edges.UnionWith(capital.GetPathTo_Euclidean(node).Edges);
            }
        }

        //Embed left_city_graph within terrain_graph
        Graph left_city_graph = GraphUtility.CreateGraph(left_edges);
        Graph left_lanes      = terrain_graph.Defleshed(left_city_graph,
                                                        GraphUtility.ObstacleMetric);

        //Do the same for right_city_graph, but alter the cost metric
        //so the right_lanes prefer a margin to exist between
        //opposing lanes of traffic.
        Graph right_city_graph = GraphUtility.CreateGraph(
            left_edges.Select(edge => new Edge(edge.B, edge.A)));

        foreach (Node node in right_city_graph.Nodes)
        {
            Edge edge;

            if (node.GetPosition() == capital.GetPosition())
            {
                edge = right_city_graph.Edges.FirstOrDefault(edge_ => edge_.B == node);
            }
            else
            {
                edge = right_city_graph.Edges.FirstOrDefault(edge_ => edge_.A == node);
            }

            Vector3 offset = (edge.A.GetPosition() - edge.B.GetPosition())
                             .Crossed(new Vector3(0, 1, 0)).normalized *OpposingTrafficMargin;

            node.SetPosition(node.GetPosition() + offset);
        }

        GraphUtility.Metric opposing_traffic_metric = GraphUtility.CreateBakedMetric(
            delegate(Node a, Node b)
        {
            System.Func <Vector3, float> get_height =
                position => Mathf.Max(0,
                                      (OpposingTrafficMargin - left_lanes.Distance(position)) /
                                      OpposingTrafficMargin);

            return(GraphUtility.CreateHeightMetric(
                       get_height,
                       height => height * 2,
                       slope => 0,
                       TerrainGridResolution * Mathf.Sqrt(2))(a, b) +

                   GraphUtility.ObstacleMetric(a, b));;
        });
        GraphUtility.Heuristics[opposing_traffic_metric] = GraphUtility.EuclideanMetric;

        Graph right_lanes = terrain_graph
                            .Defleshed(right_city_graph, opposing_traffic_metric);

        //Merge duplicate nodes
        foreach (Node node in left_lanes.Nodes.ToList())
        {
            foreach (Node other_node in right_lanes.Nodes)
            {
                if (node.GetPosition() != other_node.GetPosition())
                {
                    continue;
                }

                left_lanes.RemoveNode(node);
                left_lanes.AddNode(other_node);

                foreach (Node neighbor in node.Neighbors)
                {
                    other_node.AddNeighbor(neighbor);
                }

                IEnumerable <Node> neighbors = left_lanes.Nodes
                                               .Where(potential_neighbor => potential_neighbor.Neighbors.Contains(node));
                foreach (Node neighbor in neighbors.ToList())
                {
                    neighbor.RemoveNeighbor(node);
                    neighbor.AddNeighbor(other_node);
                }
            }
        }

        //Create "bridges" from one highway system to the other.
        int  bridge_count    = 0;
        bool is_forward_edge = true;

        foreach (Node node in left_lanes.Nodes)
        {
            if ((bridge_count++ % 4) == 0)
            {
                Node other_node = right_lanes.GetNearestNode(
                    other_node_ => is_forward_edge ? GraphUtility.ObstacleMetric(node, other_node_) :
                    GraphUtility.ObstacleMetric(other_node_, node),

                    other_node_ => node != other_node_ &&
                    !node.Neighbors.Contains(other_node_) &&
                    !other_node_.Neighbors.Contains(node));

                if (is_forward_edge)
                {
                    node.AddNeighbor(other_node);
                }
                else
                {
                    other_node.AddNeighbor(node);
                }

                is_forward_edge = !is_forward_edge;
            }
        }

        Highways = GraphUtility.CreateGraph(
            left_lanes.Edges.Union(right_lanes.Edges));

        //Fully connect the endpoints of paths because their connectivity
        //tends to be problematic.
        HashSet <Node> endpoints = new HashSet <Node>(
            left_city_graph.Nodes.Union(right_city_graph.Nodes)
            .Select(node => Highways.GetNearestNode(node)));

        foreach (Node node in endpoints)
        {
            foreach (Node other_node in Highways.Nodes)
            {
                if (node != other_node &&
                    node.Distance(other_node) < 1.2f * Mathf.Sqrt(2) * TerrainGridResolution)
                {
                    node.AddNeighbor(other_node);
                }
            }
        }
    }
Exemple #6
0
    //Assumes admissible, consistant heuristic
    public static Path GetPathTo(this Node source,
                                 Node destination,
                                 GraphUtility.Metric metric)
    {
        //"Guest" destinations aren't actually in the graph, but we can
        //still path to them by pretending they are everyone's neighbor
        bool destination_is_guest = !source.IsConnectedTo(destination);

        Dictionary <Node, Node>  previous_node  = new Dictionary <Node, Node>();
        Dictionary <Node, float> cost_to_source = new Dictionary <Node, float>();
        Dictionary <Node, float> cost_to_destination_estimate = new Dictionary <Node, float>();

        System.Func <Node, float> GetPathCostEstimate =
            node => cost_to_source[node] + cost_to_destination_estimate[node];

        SortedSet <Node> open_set = new SortedSet <Node>(
            Comparer <Node> .Create((a, b) => GetPathCostEstimate(a).CompareTo(GetPathCostEstimate(b))));
        HashSet <Node> closed_set = new HashSet <Node>();

        open_set.Add(source);
        previous_node[source]  = null;
        cost_to_source[source] = 0;
        cost_to_destination_estimate[source] = 0;

        while (open_set.Count > 0)
        {
            Node node = open_set.First();
            open_set.Remove(node);
            closed_set.Add(node);

            if (node == destination)
            {
                break;
            }

            IEnumerable <Node> neighbors = node.Neighbors;
            if (destination_is_guest)
            {
                neighbors = neighbors.Union(Utility.List(destination));
            }

            foreach (Node neighbor in neighbors)
            {
                if (closed_set.Contains(neighbor))
                {
                    continue;
                }

                float neighbor_to_source_cost = metric(node, neighbor) + cost_to_source[node];

                bool contains = cost_to_source.ContainsKey(neighbor);

                if (cost_to_source.ContainsKey(neighbor))
                {
                    if (neighbor_to_source_cost > cost_to_source[neighbor])
                    {
                        continue;
                    }
                    else
                    {
                        open_set.Remove(neighbor);
                    }
                }

                previous_node[neighbor]  = node;
                cost_to_source[neighbor] = neighbor_to_source_cost;
                cost_to_destination_estimate[neighbor] =
                    GraphUtility.Heuristics[metric](neighbor, destination);

                open_set.Add(neighbor);
            }
        }

        if (!previous_node.ContainsKey(destination))
        {
            return(null);
        }

        return(new Path(Utility.List(destination, node => previous_node[node]).Reversed()));
    }