コード例 #1
0
    /// <summary>
    /// Returns the middle point and angle of a border to an adjacent region. The angle always points to the direction of this region.
    /// </summary>
    public Tuple <Vector2, float> GetBorderCenterPositionTo(Region otherRegion)
    {
        if (!AdjacentRegions.Contains(otherRegion))
        {
            throw new Exception("Other region is not adjacent to this region");
        }

        // Find all borders between the two regions
        List <Border> borders = Borders.Where(x => x.Regions.Contains(otherRegion)).ToList();

        // Split borders into clusters and take longest cluster
        List <List <Border> > clusters       = PolygonMapFunctions.FindBorderClusters(borders);
        List <Border>         longestCluster = clusters.OrderByDescending(x => x.Sum(y => y.Length)).First();

        // Find center of longest cluster
        Tuple <Vector2, float> center = PolygonMapFunctions.FindBorderCenter(longestCluster);

        // Swap angle if the border was reversed
        Vector2 testPoint = center.Item1 + new Vector2(Mathf.Sin(Mathf.Deg2Rad * center.Item2) * 0.01f, Mathf.Cos(Mathf.Deg2Rad * center.Item2) * 0.01f);

        if (!GeometryFunctions.IsPointInPolygon4(Polygon.Nodes.Select(x => x.Vertex).ToList(), testPoint))
        {
            center = new Tuple <Vector2, float>(center.Item1, center.Item2 + 180);
        }

        return(center);
    }
コード例 #2
0
        public static River CreateRiverObject(GraphPath riverPath, PolygonMapGenerator PMG)
        {
            //Debug.Log("Creating mesh for river with " + riverPath.Nodes.Count + " points");

            // Calculate vertices of river polygon
            List <Vector2> polygonVerticesHalf1 = new List <Vector2>();
            List <Vector2> polygonVerticesHalf2 = new List <Vector2>();

            for (int i = 1; i < riverPath.Nodes.Count - 1; i++)
            {
                Vector2 startPoint = riverPath.Nodes[i - 1].Vertex;
                Vector2 thisPoint  = riverPath.Nodes[i].Vertex;
                Vector2 nextPoint  = riverPath.Nodes[i + 1].Vertex;

                float startWidth = riverPath.Nodes[i - 1].RiverWidth;
                float endWidth   = riverPath.Nodes[i].RiverWidth;

                //Debug.Log("River point " + i + ": startWidth = " + startWidth + ", endWidth = " + endWidth);

                if (i == 1) // Add two starting points
                {
                    Vector2 rotatedVector = GeometryFunctions.RotateVector((thisPoint - startPoint).normalized * startWidth, 90);
                    polygonVerticesHalf1.Add(startPoint + rotatedVector);
                    polygonVerticesHalf2.Add(startPoint - rotatedVector);
                }

                polygonVerticesHalf1.Add(GeometryFunctions.GetOffsetIntersection(startPoint, thisPoint, nextPoint, startWidth, endWidth, true));
                polygonVerticesHalf2.Add(GeometryFunctions.GetOffsetIntersection(startPoint, thisPoint, nextPoint, startWidth, endWidth, false));

                if (i == riverPath.Nodes.Count - 2) // Add two ending points (calculate river delta by taking intersecion between river and shoreline
                {
                    GraphNode lastNode = riverPath.Nodes.Last();
                    List <GraphConnection> shoreDelta       = lastNode.Connections.Where(x => x.Type == BorderType.Shore).ToList();
                    List <GraphNode>       riverDeltaPoints = new List <GraphNode>();
                    foreach (GraphConnection delta in shoreDelta)
                    {
                        if (delta.StartNode == lastNode)
                        {
                            riverDeltaPoints.Add(delta.EndNode);
                        }
                        else
                        {
                            riverDeltaPoints.Add(delta.StartNode);
                        }
                    }

                    Vector2 endPoint1, endPoint2;

                    // BUG: this method doesn't work 100%
                    GraphPolygon firstPolygon = riverPath.Nodes[i].Polygons.FirstOrDefault(x => GeometryFunctions.IsPointInPolygon4(x.Nodes.Select(x => x.Vertex).ToList(), polygonVerticesHalf1.Last()));
                    if (firstPolygon == null)
                    {
                        throw new Exception("Couldn't find direction of river delta. is river too short? length = " + riverPath.Nodes.Count);
                    }

                    bool addDeltaMidPoint = true;
                    if (riverDeltaPoints[0].Polygons.Contains(firstPolygon))
                    {
                        endPoint1 = GeometryFunctions.GetOffsetIntersection(thisPoint, nextPoint, riverDeltaPoints[0].Vertex, endWidth, 0f, true);
                        endPoint2 = GeometryFunctions.GetOffsetIntersection(thisPoint, nextPoint, riverDeltaPoints[1].Vertex, endWidth, 0f, false);

                        if (!GeometryFunctions.IsPointOnLineSegment(endPoint1, riverDeltaPoints[0].Vertex, lastNode.Vertex))
                        {
                            endPoint1        = lastNode.Vertex;
                            addDeltaMidPoint = false;
                        }
                        if (!GeometryFunctions.IsPointOnLineSegment(endPoint2, riverDeltaPoints[1].Vertex, lastNode.Vertex))
                        {
                            endPoint2        = lastNode.Vertex;
                            addDeltaMidPoint = false;
                        }
                    }
                    else
                    {
                        endPoint1 = GeometryFunctions.GetOffsetIntersection(thisPoint, nextPoint, riverDeltaPoints[1].Vertex, endWidth, 0f, true);
                        endPoint2 = GeometryFunctions.GetOffsetIntersection(thisPoint, nextPoint, riverDeltaPoints[0].Vertex, endWidth, 0f, false);

                        if (!GeometryFunctions.IsPointOnLineSegment(endPoint1, riverDeltaPoints[1].Vertex, lastNode.Vertex))
                        {
                            endPoint1        = lastNode.Vertex;
                            addDeltaMidPoint = false;
                        }
                        if (!GeometryFunctions.IsPointOnLineSegment(endPoint2, riverDeltaPoints[0].Vertex, lastNode.Vertex))
                        {
                            endPoint2        = lastNode.Vertex;
                            addDeltaMidPoint = false;
                        }
                    }

                    polygonVerticesHalf1.Add(endPoint1);
                    if (addDeltaMidPoint)
                    {
                        polygonVerticesHalf1.Add(lastNode.Vertex);
                    }
                    polygonVerticesHalf2.Add(endPoint2);
                }
            }

            polygonVerticesHalf2.Reverse();
            List <Vector2> polygonVertices = polygonVerticesHalf1;

            polygonVertices.AddRange(polygonVerticesHalf2);

            List <Vector2> polygonVerticesList = polygonVertices.ToList();

            if (GeometryFunctions.IsClockwise(polygonVerticesList))
            {
                polygonVerticesList.Reverse();
            }

            // Create object
            GameObject riverObject = MeshGenerator.GeneratePolygon(polygonVerticesList, PMG, layer: PolygonMapGenerator.LAYER_RIVER);

            River river = riverObject.AddComponent <River>();

            river.Init(riverPath.Nodes.Select(x => x.BorderPoint).ToList(), riverPath.Connections.Select(x => x.Border).ToList(), riverPath.Polygons.Select(x => x.Region).ToList());

            riverObject.GetComponent <MeshRenderer>().material.color = Color.red;

            riverObject.name = "River";

            return(river);
        }
コード例 #3
0
        public static GameObject LabelPolygon(List <List <GraphNode> > polygonBorders, string label, Color color)
        {
            // 1. Compute voronoi of given points with "VoronoiLib"
            List <FortuneSite> points = new List <FortuneSite>();
            List <GraphNode>   nodes  = new List <GraphNode>();

            foreach (List <GraphNode> polygonBorder in polygonBorders)
            {
                foreach (GraphNode n in polygonBorder)
                {
                    if (!nodes.Contains(n))
                    {
                        nodes.Add(n);
                    }
                }
            }
            foreach (GraphNode n in nodes)
            {
                points.Add(new FortuneSite(n.Vertex.x, n.Vertex.y));
            }

            float margin = 0.2f;
            float minX   = nodes.Min(x => x.Vertex.x) - margin;
            float maxX   = nodes.Max(x => x.Vertex.x) + margin;
            float minY   = nodes.Min(x => x.Vertex.y) - margin;
            float maxY   = nodes.Max(x => x.Vertex.y) + margin;
            LinkedList <VEdge> voronoi = FortunesAlgorithm.Run(points, minX, minY, maxX, maxY);

            // 2. Remove all edges that have either start or end outside of polygon
            List <Vector2> vertices      = nodes.Select(x => x.Vertex).ToList();
            List <VEdge>   edgesToRemove = new List <VEdge>();

            foreach (VEdge edge in voronoi)
            {
                if (!GeometryFunctions.IsPointInPolygon4(vertices, new Vector2((float)edge.Start.X, (float)edge.Start.Y)) || !GeometryFunctions.IsPointInPolygon4(vertices, new Vector2((float)edge.End.X, (float)edge.End.Y)))
                {
                    edgesToRemove.Add(edge);
                }
            }
            foreach (VEdge edge in edgesToRemove)
            {
                voronoi.Remove(edge);
            }

            // DEBUG voronoi
            foreach (VEdge edge in voronoi)
            {
                Debug.DrawLine(new Vector3((float)edge.Start.X, 0f, (float)edge.Start.Y), new Vector3((float)edge.End.X, 0f, (float)edge.End.Y), Color.red, 30);
            }

            // 3. Turn remaining edges into a graph (create a list of for each point representing the points it is connected to)
            Dictionary <VPoint, List <VPoint> > voronoiGraph = new Dictionary <VPoint, List <VPoint> >();

            foreach (VEdge edge in voronoi)
            {
                // handle start point
                if (!voronoiGraph.ContainsKey(edge.Start))
                {
                    voronoiGraph.Add(edge.Start, new List <VPoint>()
                    {
                        edge.End
                    });
                }
                else if (!voronoiGraph[edge.Start].Contains(edge.End))
                {
                    voronoiGraph[edge.Start].Add(edge.End);
                }

                // handle end point
                if (!voronoiGraph.ContainsKey(edge.End))
                {
                    voronoiGraph.Add(edge.End, new List <VPoint>()
                    {
                        edge.Start
                    });
                }
                else if (!voronoiGraph[edge.End].Contains(edge.Start))
                {
                    voronoiGraph[edge.End].Add(edge.Start);
                }
            }

            // 4. Find longest path (with consideration for straightness) between two leaves in graph - this is the centerline
            Dictionary <VPoint, List <VPoint> > voronoiLeaves = voronoiGraph.Where(x => x.Value.Count == 1).ToDictionary(x => x.Key, x => x.Value);

            float         curvePenalty             = 0.00007f;
            float         longestDistance          = float.MinValue;
            List <VPoint> centerLine               = new List <VPoint>();
            float         longestDistanceNoPenalty = float.MinValue;
            List <VPoint> centerLineNoPenalty      = new List <VPoint>();

            foreach (KeyValuePair <VPoint, List <VPoint> > startPoint in voronoiLeaves)
            {
                foreach (KeyValuePair <VPoint, List <VPoint> > endPoint in voronoiLeaves)
                {
                    if (startPoint.Key == endPoint.Key)
                    {
                        continue;
                    }

                    List <VPoint> leavesPath = GetShortestPath(new List <VPoint>()
                    {
                        startPoint.Key
                    }, endPoint.Key, voronoiGraph);
                    if (leavesPath == null)
                    {
                        continue;
                    }
                    float distanceWithPenalty = GetPathDistance(leavesPath, curvePenalty);
                    if (distanceWithPenalty > longestDistance)
                    {
                        longestDistance = distanceWithPenalty;
                        centerLine      = leavesPath;
                    }

                    float distanceNoPenalty = GetPathDistance(leavesPath, 0f);
                    if (distanceNoPenalty > longestDistanceNoPenalty)
                    {
                        longestDistanceNoPenalty = distanceNoPenalty;
                        centerLineNoPenalty      = leavesPath;
                    }
                }
            }

            // If the straight centerline is too short, take the longer curvy one instead
            if (GetPathDistance(centerLine, 0f) < 0.4f * GetPathDistance(centerLineNoPenalty, 0f))
            {
                centerLine = centerLineNoPenalty;
            }

            // 5. Smoothen the centerline
            int           smoothSteps      = 5;
            List <VPoint> smoothCenterLine = new List <VPoint>();

            smoothCenterLine.AddRange(centerLine);
            for (int i = 0; i < smoothSteps; i++)
            {
                smoothCenterLine = SmoothLine(smoothCenterLine);
            }


            // DEBUG centerline
            //Debug.Log("Longest path without curve penalty: " + GetPathDistance(centerLineNoPenalty, 0f));
            //Debug.Log("Longest path with curve penalty: " + GetPathDistance(centerLine, curvePenalty));
            //for (int i = 1; i < centerLineNoPenalty.Count; i++) Debug.DrawLine(new Vector3((float)centerLineNoPenalty[i - 1].X, 0f, (float)centerLineNoPenalty[i - 1].Y), new Vector3((float)centerLineNoPenalty[i].X, 0f, (float)centerLineNoPenalty[i].Y), Color.blue, 30);
            //for (int i = 1; i < centerLine.Count; i++) Debug.DrawLine(new Vector3((float)centerLine[i - 1].X, 0f, (float)centerLine[i - 1].Y), new Vector3((float)centerLine[i].X, 0f, (float)centerLine[i].Y), Color.red, 30);
            for (int i = 1; i < smoothCenterLine.Count; i++)
            {
                Debug.DrawLine(new Vector3((float)smoothCenterLine[i - 1].X, 0f, (float)smoothCenterLine[i - 1].Y), new Vector3((float)smoothCenterLine[i].X, 0f, (float)smoothCenterLine[i].Y), Color.blue, 30);
            }

            // 6. Make sure the path goes from left to right
            double xChange = 0;

            for (int i = 1; i < smoothCenterLine.Count; i++)
            {
                xChange += smoothCenterLine[i].X - smoothCenterLine[i - 1].X;
            }
            if (xChange < 0)
            {
                smoothCenterLine.Reverse();
            }


            // 7. Place text along centerline
            return(DrawTextAlongPath(label, smoothCenterLine, color));
        }