Beispiel #1
0
        private static void AddHorizontalBorder(BoostVoronoi voronoi, RTree <Edge> wallsTree, IReadOnlyList <int> border, int y, int maxX)
        {
            var index = 0;

            if (!border.Any())
            {
                voronoi.AddSegment(0, y, maxX, y);
                wallsTree.Insert(new Edge {
                    Start = new Point2D(0, y), End = new Point2D(maxX, y)
                }, new RTree <Edge> .Envelope(0, y, maxX, y));
                return;
            }

            var firstX = border[index++];

            if (firstX != 0)
            {
                var x2 = index >= border.Count ? maxX : border[index++];
                voronoi.AddSegment(firstX, y, x2, y);
                wallsTree.Insert(new Edge {
                    Start = new Point2D(firstX, y), End = new Point2D(x2, y)
                }, new RTree <Edge> .Envelope(firstX, y, x2, y));
            }

            while (index < border.Count)
            {
                var x1 = border[index++];
                var x2 = index >= border.Count ? maxX : border[index++];
                voronoi.AddSegment(x1, y, x2, y);
                wallsTree.Insert(new Edge {
                    Start = new Point2D(x1, y), End = new Point2D(x2, y)
                }, new RTree <Edge> .Envelope(x1, y, x2, y));
            }
        }
Beispiel #2
0
        private static void AddVerticalBorder(BoostVoronoi voronoi, RTree <Edge> wallsTree, IReadOnlyList <int> border, int x, int maxY)
        {
            var index = 0;

            if (!border.Any())
            {
                voronoi.AddSegment(x, 0, x, maxY);
                wallsTree.Insert(new Edge {
                    Start = new Point2D(x, 0), End = new Point2D(x, maxY)
                }, new RTree <Edge> .Envelope(x, 0, x, maxY));
                return;
            }

            var firstY = border[index++];

            if (firstY != 0)
            {
                var y2 = index >= border.Count ? maxY : border[index++];
                voronoi.AddSegment(x, firstY, x, y2);
                wallsTree.Insert(new Edge {
                    Start = new Point2D(x, firstY), End = new Point2D(x, y2)
                }, new RTree <Edge> .Envelope(x, firstY, x, y2));
            }

            while (index < border.Count)
            {
                var y1 = border[index++];
                var y2 = index >= border.Count ? maxY : border[index++];
                voronoi.AddSegment(x, y1, x, y2);
                wallsTree.Insert(new Edge {
                    Start = new Point2D(x, y1), End = new Point2D(x, y2)
                }, new RTree <Edge> .Envelope(x, y1, x, y2));
            }
        }
Beispiel #3
0
        public static IReadOnlyList <TerrainNode> Analyze <TNode>(Map2D <TNode> map, Func <TNode, bool> isBlocking, int pruneDistanceToObstacleSquared, int maxDistanceToMergeSquared)
        {
            var tracingResult = ComponentLabeling.Trace(map, node => isBlocking(node) ? 1 : 0);

            var contours = new List <List <Point2D> >();

            foreach (var blob in tracingResult.Blobs)
            {
                contours.AddRange(
                    new[] { blob.ExternalContour }.Concat(blob.InternalContours).Select(
                        points => DouglasPeucker.Simplify(points.ToList(), 2 * 2, DistanceSquared)));
            }

            var edges        = new List <Edge>();
            var leftBorder   = new List <int>();
            var rightBorder  = new List <int>();
            var topBorder    = new List <int>();
            var bottomBorder = new List <int>();
            var wallsTree    = new RTree <Edge>(5, 9);

            using (var voronoi = new BoostVoronoi())
            {
                foreach (var contour in contours)
                {
                    var startPoint = contour.First();
                    foreach (var point in contour.Skip(1))
                    {
                        voronoi.AddSegment((int)startPoint.X, (int)startPoint.Y, (int)point.X, (int)point.Y);
                        wallsTree.Insert(new Edge {
                            Start = startPoint, End = point
                        }, new RTree <Edge> .Envelope(startPoint, point));

                        if ((int)startPoint.X == 0 && (int)point.X == 0)
                        {
                            leftBorder.Add((int)startPoint.Y);
                            leftBorder.Add((int)point.Y);
                        }

                        if ((int)startPoint.X == map.Width - 1 && (int)point.X == map.Width - 1)
                        {
                            rightBorder.Add((int)startPoint.Y);
                            rightBorder.Add((int)point.Y);
                        }

                        if ((int)startPoint.Y == 0 && (int)point.Y == 0)
                        {
                            topBorder.Add((int)startPoint.X);
                            topBorder.Add((int)point.X);
                        }

                        if ((int)startPoint.Y == map.Height - 1 && (int)point.Y == map.Height - 1)
                        {
                            bottomBorder.Add((int)startPoint.X);
                            bottomBorder.Add((int)point.X);
                        }

                        startPoint = point;
                    }
                }

                AddVerticalBorder(voronoi, wallsTree, leftBorder.OrderBy(x => x).ToList(), 0, map.Height - 1);
                AddVerticalBorder(voronoi, wallsTree, rightBorder.OrderBy(x => x).ToList(), map.Width - 1, map.Height - 1);
                AddHorizontalBorder(voronoi, wallsTree, topBorder.OrderBy(x => x).ToList(), 0, map.Width - 1);
                AddHorizontalBorder(voronoi, wallsTree, bottomBorder.OrderBy(x => x).ToList(), map.Height - 1, map.Width - 1);

                var visitedTwins = new List <long>();
                voronoi.Construct();
                for (long i = 0; i < voronoi.CountEdges; i++)
                {
                    var edge = voronoi.GetEdge(i);

                    if (!edge.IsPrimary || !edge.IsFinite || visitedTwins.Contains(edge.Twin))
                    {
                        continue;
                    }

                    visitedTwins.Add(edge.Twin);

                    var start = voronoi.GetVertex(edge.Start);
                    var end   = voronoi.GetVertex(edge.End);

                    if (double.IsNaN(start.X) || double.IsNaN(start.Y) || double.IsNaN(end.X) || double.IsNaN(end.Y))
                    {
                        continue;
                    }

                    if (edges.Any(
                            e => (int)e.Start.X == (int)start.X && (int)e.Start.Y == (int)start.Y &&
                            (int)e.End.X == (int)end.X && (int)e.End.Y == (int)end.Y ||
                            (int)e.Start.X == (int)end.X && (int)e.Start.Y == (int)end.Y &&
                            (int)e.End.X == (int)start.X && (int)e.End.Y == (int)start.Y))
                    {
                        continue;
                    }

                    edges.Add(new Edge
                    {
                        Start = new Point2D((int)start.X, (int)start.Y),
                        End   = new Point2D((int)end.X, (int)end.Y)
                    });
                }
            }

            var walkableEdges = edges.Where(edge => IsOnNonBlocking(edge, map, isBlocking)).ToList();

            var nodes = new List <InternalNode>();

            foreach (var node in walkableEdges)
            {
                if (node.Start == node.End)
                {
                    continue;
                }

                var startNode = nodes.Find(x => x.Point == node.Start);
                if (startNode == null)
                {
                    startNode = CreateNode(wallsTree, node.Start);
                    nodes.Add(startNode);
                }

                var endNode = nodes.Find(x => x.Point == node.End);
                if (endNode == null)
                {
                    endNode = CreateNode(wallsTree, node.End);
                    nodes.Add(endNode);
                }

                startNode.Neighbors.Add(endNode);
                endNode.Neighbors.Add(startNode);
            }

            PruneNodes(nodes, pruneDistanceToObstacleSquared);

            GetPointsOfInterest(nodes, pruneDistanceToObstacleSquared, maxDistanceToMergeSquared);

            var mergedNodes = MergeNodes(nodes, maxDistanceToMergeSquared);

            return(MapToTerrainNodes(mergedNodes, wallsTree));
        }