コード例 #1
0
ファイル: MeshDrawer.cs プロジェクト: bethge/GalaticChaos
        public void DrawPolygon(Polygon polygon)
        {
            List<MeshNode> cornerNodes = polygon.GetRectCorners();
            List<Vector2> cornerVectors = new List<Vector2>();
            for (int i = 0; i < 4; i++)
            {
                cornerVectors.Add(ToScreenCoords(cornerNodes[i].mVector));
            }
            for (int i = 0; i <= 1; i++)
            {
                CodeTest.DrawLine(cornerVectors[i], cornerVectors[i + 2], 1, Color.Purple);
            }

            foreach (Edge e in polygon.GetEdges())
            {
                if (e.GetContrahent() == null)
                    DrawSingleEdge(e);
                else
                    DrawEdge(e);
            }
            foreach (MeshNode node in polygon.GetNodes())
            {
                DrawNode(node);
            }
        }
コード例 #2
0
ファイル: NavigationMesh.cs プロジェクト: bethge/GalaticChaos
 /// <summary>
 /// Adds a polygon to the mesh.
 /// </summary>
 public void AddPolygon(Polygon toAdd)
 {
     Debug.Assert(toAdd.GetEdges() != null);
     int[] indexes = GetCoveredSectorIndexes(toAdd);
     for (int y = indexes[1]; y <= indexes[3]; y++)
         for (int x = indexes[0]; x <= indexes[2]; x++ )
         {
             Debug.Assert(!mSectors[x, y].Contains(toAdd));
             mSectors[x,y].Add(toAdd);
         }
 }
コード例 #3
0
ファイル: NavigationMesh.cs プロジェクト: bethge/GalaticChaos
 /// <summary>
 /// Checks if given polygons form a concave union.
 /// The polygons must be neighboured and rectangular.
 /// </summary>
 private bool AreConcaveRectSpeedUp(Polygon p1, Polygon p2)
 {
     // we already assume they are neighboured
     // ReSharper disable CompareOfFloatsByEqualityOperator
     // Since we assume they are neighboured, they will have to share the same
     // MeshNodes at their common border. Therefore, the value, coming from the same
     // instance field, must be exactly equal.
     if ((p1.Xmin == p2.Xmin && p1.Xmax == p2.Xmax)
         || (p1.Ymin == p2.Ymin && p1.Ymax == p2.Ymax))
     // ReSharper restore CompareOfFloatsByEqualityOperator
         return true;
     return false;
 }
コード例 #4
0
ファイル: NavigationMesh.cs プロジェクト: bethge/GalaticChaos
        /// <summary>
        /// Unites two given polygons into one and returns it. The two polygons
        /// are deleted. Optionally applies a cleanup to the new polygon.
        /// </summary>
        /// <param name="unionEdges">the edges of the new united polygon</param>
        /// <param name="polygon1">the first of the polygons that are to be united</param>
        /// <param name="polygon2">the second of the polygons that are to be united</param>
        /// <param name="allowCleanUp">if the edges of the united polygon should
        ///  be cleaned up</param>
        /// <returns>the new united polygon</returns>
        private Polygon ApplyPolygonUnion(List<Edge> unionEdges, Polygon polygon1, Polygon polygon2, bool allowCleanUp)
        {
            Debug.Assert(! polygon1.CanBeCleanedUp(), "Cannot unite with a dirty polygon!");
            Debug.Assert(!polygon2.CanBeCleanedUp(), "Cannot unite with a dirty polygon!");
            Polygon unionPolygon = new Polygon(unionEdges);
            unionPolygon.ImposeOwnership();

            polygon1.Delete(true);
            RemovePolygon(polygon1);

            polygon2.Delete(true);
            RemovePolygon(polygon2);

            AddPolygon(unionPolygon);

            if (allowCleanUp) unionPolygon.CleanUp(true);
            return unionPolygon;
        }
コード例 #5
0
        /// <summary>
        /// Returns a new mesh instance. This instance has its polygons auto-generated according to
        /// the given set "blocked" of impassable (int,int)-points. The largest possibly passable area will be
        /// a rectangle from (0,0) to (width,height).
        /// </summary>
        /// <param name="blocked">contains all integer points that are impassable</param>
        /// <param name="remainingPolygons">The polygons that still exist within area</param>
        /// <param name="coveredByPolygonBefore">the nodes that are covered by remainingPolygons</param>
        /// <param name="scale">the mesh scale</param>
        /// <param name="area">the area that should be filled with new polygons</param>
        /// <param name="createdNodes">the still existing nodes that must be integrated</param>
        /// <param name="unite">if everything in the area should be united at last</param>
        /// <returns>a new instance of Navigation Mesh that contains all freshly created polygons (but not remainingPolygons)</returns>
        public static NavigationMesh CreateInstanceFromBlockmap(PointSet<int> blocked, List<Polygon> remainingPolygons, PointSet<int> coveredByPolygonBefore, int scale, Rectangle area, List<MeshNode> createdNodes, bool unite = true)
        {
            GlobalValues globalValues = GlobalValues.GetInstance();
            PointSet<int> coveredByPremarkedPolygon = new PointSet<int>();
            if (globalValues.mShowMeshCreation)
            {
                CodeTest.mRects2ToDraw.Add(area);
                //CodeTest.mSetsToDraw.Add(coveredByPolygonBefore); // slows drawing down for big meshes
                //CodeTest.mSetsToDraw.Add(coveredByPremarkedPolygon);
                //CodeTest.mSetsToDraw.Add(blocked);
            }

            Debug.Assert(area.Width > 0 && area.Height > 0 && area.X >= 0 && area.Y >= 0);
            int areaWidth = (area.Width / scale);
            int areaHeight = (area.Height / scale);
            int areaOffsetX = (area.X / scale);
            int areaOffsetY = (area.Y / scale);

            PointSet<int> nodeToCreate = new PointSet<int>();
            PointSet<int> nodeCreated = new PointSet<int>();
            foreach (MeshNode node in createdNodes)
            {
                int x = ((int)(node.mVector.X)) / scale;
                int y = ((int)(node.mVector.Y)) / scale;
                nodeToCreate.Add(x, y);
                nodeCreated.Add(x, y);
            }

            #if DEBUG
            if (areaWidth < 20 && areaHeight < 20)
            {
                Debug.WriteLine("blocked (part):");
                NavigationMesh.PrintPointSet(blocked, areaOffsetX, areaOffsetY, areaWidth + 1, areaHeight + 1);
            }
            #endif

            List<Rectangle> premarkedPolygons = new List<Rectangle>();
            // a point(a,b) in coveredByPolygon indicates that there is already
            // a registered polygon which covers at least the square (a,b)-(a+1,b+1)

            for (int y = areaOffsetY; y < areaOffsetY + areaHeight; y++)
            {
                for (int x = areaOffsetX; x < areaOffsetX + areaWidth; x++)
                {
                    // we wanna create a new rectangular polygon(x,y,*,*)
                    // as big as possible.
                    if (!coveredByPolygonBefore.Contains(x, y) &&
                        !coveredByPremarkedPolygon.Contains(x, y) &&
                        !blocked.Contains(x, y) &&
                        !blocked.Contains(x + 1, y) &&
                        !blocked.Contains(x, y + 1) &&
                        !blocked.Contains(x + 1, y + 1))
                    {
                        int polyWidth = 1;
                        int polyHeight = 1;

                        while (x + polyWidth <= areaOffsetX + areaWidth
                               && HorizontalFree(blocked, x, y, polyWidth, polyHeight)
                               && HorizontalFree(coveredByPolygonBefore, x, y, polyWidth - 1, polyHeight - 1)
                               && HorizontalFree(coveredByPremarkedPolygon, x, y, polyWidth - 1, polyHeight - 1)
                               && y + polyHeight <= areaOffsetY + areaHeight
                               && VerticalFree(blocked, x, y, polyWidth, polyHeight)
                               && VerticalFree(coveredByPolygonBefore, x, y, polyWidth - 1, polyHeight - 1)
                               && VerticalFree(coveredByPremarkedPolygon, x, y, polyWidth - 1, polyHeight - 1))
                        {
                            if (globalValues.mShowMeshCreation)
                            {
                                Rectangle rectToDraw = new Rectangle(scale * x, scale * y, scale * polyWidth, scale * polyHeight);
                                CodeTest.mRectsToDraw.Add(rectToDraw);
                                CodeTest.DrawNow();
                                CodeTest.mRectsToDraw.Remove(rectToDraw);
                            }
                            polyWidth++;
                            polyHeight++;
                        }
                        polyWidth--;
                        polyHeight--;
                        Debug.Assert(polyWidth > 0 && polyHeight > 0 && polyWidth == polyHeight);
                        // We won't create the polygon now because there is a little
                        // catch (see below), but we are keeping it in mind.
                        // => fill premarkedPolygons now...
                        if (globalValues.mShowMeshCreation)
                        {
                            Rectangle premarkedFullScaled = new Rectangle(scale * x, scale * y, scale * polyWidth, scale * polyHeight);
                            CodeTest.mRectsToDraw.Add(premarkedFullScaled);
                        }
                        Rectangle premarked = new Rectangle(x, y, polyWidth, polyHeight);
                        premarkedPolygons.Add(premarked);
                        // ...and mark nodes as used:
                        nodeToCreate.Add(x, y);
                        nodeToCreate.Add(x + polyWidth, y);
                        nodeToCreate.Add(x + polyWidth, y + polyHeight);
                        nodeToCreate.Add(x, y + polyHeight);

                        // register in coveredByPolygon:
                        for (int i = x; i < x + polyWidth; i++)
                        {
                            for (int j = y; j < y + polyHeight; j++)
                            {
                                coveredByPremarkedPolygon.Add(i, j);
                            }
                        }
                    }
                }
            }
            Debug.WriteLine("premarked polygons:" + premarkedPolygons.Count);
            if (globalValues.mShowMeshCreation)
            {
                for (int i = 0; i < premarkedPolygons.Count; i++)
                        CodeTest.mRectsToDraw.RemoveAt(CodeTest.mRectsToDraw.Count - 1);
            }
            // Now that we have full knowledge about ALL nodes to create, we can
            // create the appropriate polygons. The catch is that sometimes it doesn't
            // suffice to create the polygon between its corners, but there are polygons
            // with more vertices in between.

            int mapWidth = GameLogic.GetInstance().GetMap().GetWidth();
            int mapHeight = GameLogic.GetInstance().GetMap().GetHeight();
            NavigationMesh result = new NavigationMesh(mapWidth, mapHeight);
            if (globalValues.mShowMeshCreation) CodeTest.mMeshesToDraw.Add(result);

            // Create each premarked polygon:
            foreach (Rectangle rectangle in premarkedPolygons)
            {
                List<Point> vertexList = GetVertexList(rectangle, nodeToCreate);
                Debug.Assert(vertexList.Count >= 4);
                List<MeshNode> nodes = new List<MeshNode>();

                // check which nodes are already created and which need to be created:
                foreach (Point point in vertexList)
                {
                    MeshNode node = RetrieveOrCreateNode(point, scale, nodeCreated, createdNodes, remainingPolygons, coveredByPolygonBefore);
                    nodes.Add(node);
                }
                // now we can chain up our nodes:
                Polygon polygon = new Polygon(nodes);
                polygon.ImposeOwnership();
                // add polygon to our mesh:
                // NOTE: WE MAY NOT ALLOW POLYGON CLEAN UP DURING MESH CONSTRUCTION!!!!
                // Damn error, I've found you at last...
                result.AddPolygon(polygon);

                if (globalValues.mShowMeshCreation) CodeTest.DrawNow();

            }

            // Now unite and clean everything: (At least I think we are allowed now...)
            if (unite) result.WholeUnionRun(true, null);

            if (globalValues.mShowMeshCreation)
            {
                CodeTest.mMeshesToDraw.Remove(result);
                CodeTest.mRects2ToDraw.Remove(area);
                //CodeTest.mSetsToDraw.Remove(coveredByPolygonBefore); // again, taken out for being slow
                //CodeTest.mSetsToDraw.Remove(coveredByPremarkedPolygon);
                //CodeTest.mSetsToDraw.Remove(blocked);
            }
            return result;
        }
コード例 #6
0
        /// <summary>
        /// For code testing purpose:
        /// Returns a new special-shaped mesh instance.
        /// </summary>
        public static NavigationMesh[] GetTestInstanceForCleanUp()
        {
            NavigationMesh[] result = new NavigationMesh[3];
            for (int x = 0; x < 3; x++)
            {
                MeshNode a = new MeshNode(0, 0);
                MeshNode b = new MeshNode(0, 10);
                MeshNode c = new MeshNode(5, 10);
                MeshNode d = new MeshNode(5, 0);
                MeshNode e = new MeshNode(0, 15);
                MeshNode f = new MeshNode(10, 15);
                MeshNode g = new MeshNode(10, 10);
                MeshNode h = new MeshNode(15, 15);
                MeshNode i = new MeshNode(15, 0);
                MeshNode j = new MeshNode(10, 0);
                Edge ab = new Edge(a, b);
                ab.ImposeOwnership();
                Edge bc = new Edge(b, c);
                bc.ImposeOwnership();
                Edge cd = new Edge(c, d);
                cd.ImposeOwnership();
                Edge da = new Edge(d, a);
                da.ImposeOwnership();

                Edge be = new Edge(b, e); be.ImposeOwnership();
                Edge ef = new Edge(e, f); ef.ImposeOwnership();
                Edge fg = new Edge(f, g); fg.ImposeOwnership();
                Edge gc = new Edge(g, c); gc.ImposeOwnership();
                Edge cb = new Edge(c, b); cb.ImposeOwnership();

                Edge fh = new Edge(f, h); fh.ImposeOwnership();
                Edge hi = new Edge(h, i); hi.ImposeOwnership();
                Edge ij = new Edge(i, j); ij.ImposeOwnership();
                Edge jg = new Edge(j, g); jg.ImposeOwnership();
                Edge gf = new Edge(g, f); gf.ImposeOwnership();

                Polygon pA = new Polygon(new List<Edge> { ab, bc, cd, da });
                pA.ImposeOwnership();
                Polygon pB = new Polygon(new List<Edge> { be, ef, fg, gc, cb });
                pB.ImposeOwnership();
                Polygon pC = new Polygon(new List<Edge> { fh, hi, ij, jg, gf });
                pC.ImposeOwnership();

                result[x] = new NavigationMesh(15,15);
                result[x].AddPolygon(pA);
                result[x].AddPolygon(pB);
                result[x].AddPolygon(pC);
            }
            return result;
        }
コード例 #7
0
ファイル: PathCleaner.cs プロジェクト: bethge/GalaticChaos
        /// <summary>
        /// Takes the result from PathFinder.CalculatePath() and returns a cleaned
        /// up path from it.
        /// </summary>
        public static Path GetCleanedUpVectorPath(AStarNode target, bool startPassable, Polygon endPolygon)
        {
            // First get the list of meshNodes:
            List<MeshNode> meshNodes = new List<MeshNode>();
            while (target != null)
            {
                meshNodes.Add(target.mNode);
                target = target.mPrecedent;
            }
            // If we don't need to reach the start point, we just remove the most recent point:
            if (!startPassable) meshNodes.RemoveAt(meshNodes.Count - 1);
            meshNodes.Reverse();

            // We're ready to kick ass!
            if (meshNodes.Count > 2)
            {   // We can only clean up with 3 or more nodes
                int uniteFromIndex = 0;
                while (uniteFromIndex < meshNodes.Count - 2)
                {
                    Debug.Assert(uniteFromIndex < meshNodes.Count - 2);

                    int i = uniteFromIndex + 2;
                    Debug.Assert(i < meshNodes.Count);

                    Stack<Edge> firstCandidateEdges = new Stack<Edge>(meshNodes[uniteFromIndex].GetPolyOwnersEdges());
                    Debug.Assert(firstCandidateEdges.Count > 0,
                                 "GetCleanedUpVectorPath(): Could not find first candidate edges!");

                    Debug.Assert(uniteFromIndex < i - 1);
                    Debug.Assert(uniteFromIndex < meshNodes.Count);
                    Debug.Assert(i < meshNodes.Count);

                    while (i < meshNodes.Count && IsCleanUpPossible(GetCleanUpEndPolygons(i, meshNodes, endPolygon),
                                                                    firstCandidateEdges,
                                                                    meshNodes[uniteFromIndex].mVector,
                                                                    meshNodes[i].mVector))
                    {
                        i++;
                    }
                    // Whatever case has broken the loop, we try to
                    // clean up from uniteFromIndex to i-1 if feasible:
                    i--;
                    if (uniteFromIndex < i - 1)
                    {
                        int removedNodeCount = i - uniteFromIndex - 1;
                        Debug.Assert(removedNodeCount > 0);
                        CleanUp(uniteFromIndex, i, meshNodes);
                    }
                    uniteFromIndex++;
                }
                // uniteFromIndex is too high to clean up anymore
            }

            // We're done:
            Stack<Vector2> stack = new Stack<Vector2>();
            // fill path backwards (it's a stack after all):
            for (int j = meshNodes.Count - 1; j >= 0; j--)
            {
                stack.Push(meshNodes[j].mVector);
            }
            return new Path(stack);
        }
コード例 #8
0
ファイル: PathFinder.cs プロジェクト: bethge/GalaticChaos
 /// <summary>
 /// Checks the end condition of our AStar algorithm.
 /// </summary>
 private bool EndConditionMet(AStarNode current, bool endPassable, Polygon endPolygon, List<MeshNode> endNodes)
 {
     if (endPassable)
     {   // use endPolygon to determine termination
         // we're done if the current node is part of the endPolygon
         return (current.mNode.mPolyOwners.Contains(endPolygon));
     }
     /*else*/
     {   // use endNodes to determine termination
         // we're done if the current node is part of endNodes:
         return (endNodes.Contains(current.mNode));
     }
 }
コード例 #9
0
ファイル: Polygon.cs プロジェクト: bethge/GalaticChaos
        /// <summary>
        /// Returns a new polygon instance that is the union of this
        /// and the given polygon. Union is at the first common edge found.
        /// Returns null if no common edge found.
        /// </summary>
        /// <param name="polygon2">the polygon with which to unite</param>
        /// <param name="commonEdge">returns this polygon's edge that reversely equals
        ///  commonEdge2 from polygon2</param>
        /// <param name="commonEdge2">returns polygon2's edge that reversely equals
        ///  commonEdge from this polygon</param>
        /// <returns>united polygon if possible, else null</returns>
        public List<Edge> GetUnion(Polygon polygon2, out Edge commonEdge, out Edge commonEdge2)
        {
            List<Edge> commonEdges = GetFirstIncidentEdge(polygon2);
            if (commonEdges == null)
            {
                commonEdge = null;
                commonEdge2 = null;
                return null;
            }

            commonEdge = commonEdges[0];
            int commonEdgeIndex = mEdges.IndexOf(commonEdge);
            commonEdge2 = commonEdges[1];
            List<Edge> edges2 = polygon2.GetEdges();
            int edge2Index = edges2.IndexOf(commonEdge2);

            List<Edge> newEdges = new List<Edge>();

            // copy from mEdges up to the index of edge:
            int length = mEdges.Count;
            for (int i = 0; i < commonEdgeIndex; i++)
            {
                Debug.Assert(mEdges[i].mEndNode == mEdges[i+1].mStartNode);
                newEdges.Add(mEdges[i]);
            }
            // copy along edges2 from and to edge2Index
            int length2 = edges2.Count;
            int j = (edge2Index + 1) % length2;
            if (commonEdgeIndex > 0) Debug.Assert(mEdges[commonEdgeIndex-1].mEndNode == edges2[j].mStartNode);

            while (edges2[j] != commonEdge2)
            {
                Debug.Assert(edges2[j].mEndNode == edges2[(j+1)%length2].mStartNode);
                newEdges.Add(edges2[j]);
                j = (j + 1) % length2;
            }
            Debug.Assert(j == edge2Index);
            j--;
            if (j < 0) j = j + length2;
            if (commonEdgeIndex+1<length) Debug.Assert(edges2[j].mEndNode == mEdges[commonEdgeIndex + 1].mStartNode);
            // now we're again in mEdges of the first polygon, copy the rest:
            for (int h = commonEdgeIndex + 1; h < length; h++)
            {
                Debug.Assert(mEdges[h].mEndNode == mEdges[(h+1)%length].mStartNode);
                newEdges.Add(mEdges[h]);
            }
            Debug.Assert(AllEdgesIncident(newEdges));
            // newEdges finished
            return newEdges;
        }
コード例 #10
0
ファイル: Polygon.cs プロジェクト: bethge/GalaticChaos
 public int CountIncidentEdges(Polygon polygon2)
 {
     int counter = 0;
     List<Edge> edges2 = polygon2.GetEdges();
     foreach (Edge edge in mEdges)
     {
         foreach (Edge edge2 in edges2)
         {
             if (edge.RecursiveReverse(edge2))
             {
                 counter++;
             }
         }
     }
     return counter;
 }
コード例 #11
0
ファイル: CodeTest.cs プロジェクト: bethge/GalaticChaos
        private static void PolygonTests()
        {
            double pi = Math.PI;
            List<MeshNode> nodes = new List<MeshNode> { new MeshNode(0, 0), new MeshNode(10, 0), new MeshNode(10, 10), new MeshNode(0, 10) };

            Polygon polygon = new Polygon(nodes);
            polygon.ImposeOwnership();
            List<Edge> edges = polygon.GetEdges();
            Debug.Assert(HelperMethods.Circa(
                HelperMethods.GetAngleDifference(edges[0].ToVector(), edges[1].ToVector()), pi / 2));
            Debug.Assert(Polygon.IsConcave(polygon.GetEdges()), "CodeTest.PolygonTests(): Polygon not concave!");
            Debug.Assert(polygon.PointInPolygon(new Vector2(10, 5), false), "CodeTest.PolygonTests(): 10,5 not in Polygon!");

            Random r = new Random();
            Vector2 point1 = new Vector2((float)(10 * r.NextDouble()), (float)(10 * r.NextDouble()));
            Debug.Assert(polygon.PointInPolygon(point1, false), "CodeTest.PolygonTests(): Random point not in Polygon!");

            Vector2 p1 = new Vector2(0, 0);
            Vector2 p2 = new Vector2(0, 10);
            Vector2 p3 = new Vector2(10, 0);
            Vector2 p4 = new Vector2(10, 10);
            Debug.Assert(polygon.PointInPolygon(p1, false), "PointInPolygon Test failed!");
            Debug.Assert(polygon.PointInPolygon(p2, false), "PointInPolygon Test failed!");
            Debug.Assert(polygon.PointInPolygon(p3, false), "PointInPolygon Test failed!");
            Debug.Assert(polygon.PointInPolygon(p4, false), "PointInPolygon Test failed!");

            List<MeshNode> nodes2 = new List<MeshNode> { nodes[1], new MeshNode(20, 0), new MeshNode(20, 10), nodes[2] };
            Polygon polygon2 = new Polygon(nodes2);
            polygon2.ImposeOwnership();
            List<Polygon> polygonNeighbours = polygon.GetAdjacentPolygons();
            List<Polygon> polygon2Neighbours = polygon2.GetAdjacentPolygons();

            Debug.Assert(polygonNeighbours.Count == 1 && polygonNeighbours.Contains(polygon2));
            Debug.Assert(polygon2Neighbours.Count == 1 && polygon2Neighbours.Contains(polygon));
            Edge commonEdge, commonEdge2;
            List<Edge> unitedEdges = polygon.GetUnion(polygon2, out commonEdge, out commonEdge2);
            Polygon unitedPolygon = new Polygon(unitedEdges);
            unitedPolygon.ImposeOwnership();
            polygon.Delete(true);
            polygon2.Delete(true);
            unitedPolygon.CleanUp(true);
            Debug.Assert(commonEdge.mStartNode == null);
            Debug.Assert(commonEdge.mEndNode == null);
            Debug.Assert(commonEdge2.mStartNode == null);
            Debug.Assert(commonEdge2.mEndNode == null);
            Debug.Assert(unitedPolygon.EdgesHaveOneOwner());
            Debug.Assert(unitedPolygon.NodesEdgesOwnerTest());
            Debug.WriteLine("Use breakpoint to check the last union test.");

            Debug.WriteLine("Polygon tests done.");
            unitedPolygon.Delete(true);
        }
コード例 #12
0
ファイル: CodeTest.cs プロジェクト: bethge/GalaticChaos
        /// <summary>
        /// Tests for Navigation Mesh and Pathfinder,
        /// including performance test.
        /// </summary>
        private static void PathfinderTest()
        {
            Debug.WriteLine("Create testMesh...");
            NavigationMesh[] testMesh = NavigationMeshFactory.GetTestInstanceForCleanUp();
            Debug.WriteLine("testMesh created. Try 1. pathfinding...");
            PathFinder.GetInstance().SetMesh(testMesh);
            Stopwatch watch = Stopwatch.StartNew();
            Path path = PathFinder.GetInstance().CalculatePath(new Vector2(1, 1), new Vector2(4, 14), typeof(Minion));
            Debug.Assert(path != null);
            watch.Stop();
            Debug.WriteLine("1. Pathfinding done in " + watch.ElapsedMilliseconds +
                " ms. Use breakpoint to check the returned path.");
            GameLogic.GetInstance().GetGameStateManager().UnloadGame();
            // ********************* second pathfinder test **************************
            Debug.WriteLine("Try 2. pathfinding on another testMesh...");
            watch = Stopwatch.StartNew();
            int width = 5000;
            int height = 5000;
            Map map = Map.GetRandomInstance(20, width, height);
            Debug.Assert(map != null);
            GameLogic.GetInstance().SetMap(map);
            Debug.Assert(GameLogic.GetInstance().GetMap() != null);
            PathFinder.GetInstance().LoadMeshFromObstructions(width, height, 5);
            watch.Stop();
            Debug.WriteLine("Mesh creation done in " + watch.ElapsedMilliseconds + " ms.");
            watch = Stopwatch.StartNew();
            // ReSharper disable RedundantAssignment
            // I want to read this out via debugger
            path = PathFinder.GetInstance().CalculatePath(new Vector2(0, 0), new Vector2(width - 1, height - 1), typeof(Minion));
            // ReSharper restore RedundantAssignment
            watch.Stop();
            Debug.WriteLine("2. Pathfinding done in " + watch.ElapsedMilliseconds +
                              " ms. Check path via breakpoint.");

            // ******************************* Extreme case: Check that a direct connection is NOT feasible **************************
            List<Polygon> endPolygons = new List<Polygon>();
            List<MeshNode> meshNodes = new List<MeshNode>();
            for (int y = 0; y < 4; y++)
            {
                for (int x = 0; x < 4; x++)
                {
                    meshNodes.Add(new MeshNode(x, y));
                }
            }
            List<Polygon> polygons = new List<Polygon>();
            for (int y = 0; y < 3; y++)
            {
                for (int x = 0; x < 3; x++)
                {
                    if (!(x == 1 && y == 1))
                    {
                        Polygon polygon = new Polygon(new List<MeshNode> { meshNodes[y * 4 + x], meshNodes[y * 4 + (x + 1)], meshNodes[(y + 1) * 4 + (x + 1)], meshNodes[(y + 1) * 4 + x] });
                        polygon.ImposeOwnership();
                        polygons.Add(polygon);
                    }
                }
            }
            int startNodeIndex = 9;
            int endNodeIndex = 6;
            endPolygons.Add(polygons[2]);

            Stack<Edge> firstCandidateEdges = new Stack<Edge>(meshNodes[startNodeIndex].GetPolyOwnersEdges());

            Debug.Assert(!PathCleaner.IsCleanUpPossible(endPolygons, firstCandidateEdges, meshNodes[startNodeIndex].mVector, meshNodes[endNodeIndex].mVector));
            GameLogic.GetInstance().GetGameStateManager().UnloadGame();

            // ******************* Direct connection test ******************
            int length = 3;
            int count = 3;
            testMesh = NavigationMeshFactory.GetTestInstance(length, length, count, count, false);
            PathFinder.GetInstance().SetMesh(testMesh);
            Debug.Assert(PathFinder.GetInstance().DirectConnection(new Vector2(2, 0), new Vector2(2, 3), typeof(Minion)), "Direct connection failed for (2,0)-(2,3)");
            Debug.Assert(PathFinder.GetInstance().DirectConnection(new Vector2(8, 1), new Vector2(3, 1), typeof(Minion)), "Direct connection failed for (8,1)-(3,1)");
            Debug.Assert(PathFinder.GetInstance().DirectConnection(new Vector2(8, 1), new Vector2(3, 3), typeof(Minion)), "Direct connection failed for (8,1)-(3,3)");
            Debug.Assert(PathFinder.GetInstance().DirectConnection(new Vector2(4, 9), new Vector2(5, 4), typeof(Minion)), "Direct connection failed for (4,9)-(5,4)");
            Random r = new Random();
            Vector2 point1 = new Vector2(r.Next(length * count + 1), r.Next(length * count + 1));
            Vector2 point2 = new Vector2(r.Next(length * count + 1), r.Next(length * count + 1));
            Debug.Assert(PathFinder.GetInstance().DirectConnection(point1, point2, typeof(Minion)), "Direct connection test failed for (" + point1.X + "," + point1.Y + ")-(" + point2.X + "," + point2.Y + ")");
            GameLogic.GetInstance().GetGameStateManager().UnloadGame();
        }
コード例 #13
0
ファイル: CodeTest.cs プロジェクト: bethge/GalaticChaos
        private static void HelperMethodsTest()
        {
            // Angle:
            Debug.Assert(HelperMethods.Circa(
            HelperMethods.GetAngleDifference(new Vector2(1, 0), new Vector2(0, -1)), -Math.PI / 2));

            // PointOnLine:
            Debug.Assert(HelperMethods.PointOnLine(new Vector2(1, 1), new Vector2(0, 0), new Vector2(2, 2)));
            Debug.Assert(HelperMethods.PointOnLine(new Vector2(1, 1), new Vector2(2, 2), new Vector2(0, 0)));
            Debug.Assert(HelperMethods.PointOnLine(new Vector2(1, 1), new Vector2(2, 0), new Vector2(0, 2)));
            Debug.Assert(!HelperMethods.PointOnLine(new Vector2(2, 0), new Vector2(1, 1), new Vector2(0, 2)));
            Debug.Assert(HelperMethods.PointOnLine(new Vector2(1, 1), new Vector2(1, 1), new Vector2(0, 0)));
            Debug.Assert(HelperMethods.PointOnLine(new Vector2(0, 2), new Vector2(0, 0), new Vector2(0, 2)));
            Debug.Assert(HelperMethods.PointOnLine(new Vector2(0, 0), new Vector2(0, 2), new Vector2(0, 0)));

            // PointInPolygon:
            // These points are all inside:
            Polygon polygon = new Polygon(new List<MeshNode> { new MeshNode(0, 0), new MeshNode(2, 0), new MeshNode(2, 2), new MeshNode(1, 2), new MeshNode(0, 2), new MeshNode(0, 1) });
            polygon.ImposeOwnership();
            foreach (MeshNode node in polygon.GetNodes())
            {
                Debug.Assert(polygon.PointInPolygon(node.mVector, false));
            }
            Debug.Assert(polygon.PointInPolygon(new Vector2(1, 0), false));
            Debug.Assert(polygon.PointInPolygon(new Vector2(1, 1), false));
            Debug.Assert(polygon.PointInPolygon(new Vector2(2, 1), false));
            // Now for some points outside:
            Debug.Assert(!polygon.PointInPolygon(new Vector2(0, -1), false));
            Debug.Assert(!polygon.PointInPolygon(new Vector2(1, -1), false));
            Debug.Assert(!polygon.PointInPolygon(new Vector2(2, -1), false));
            Debug.Assert(!polygon.PointInPolygon(new Vector2(0, 3), false));
            Debug.Assert(!polygon.PointInPolygon(new Vector2(1, 3), false));
            Debug.Assert(!polygon.PointInPolygon(new Vector2(2, 3), false));

            polygon.Delete(true);
        }
コード例 #14
0
ファイル: CodeTest.cs プロジェクト: bethge/GalaticChaos
        // ReSharper restore UnusedMember.Local
        // ReSharper disable UnusedMember.Local
        // Sometimes I want to call this test for debugging, so it's okay that it's currently
        // not used.
        private static void CreateInstanceFromBlockmapTest()
        {
            PointSet<int> blocked = new PointSet<int>();
            blocked.Add(12,12);
            PointSet<int> coveredByPolygon = new PointSet<int>();
            coveredByPolygon.Add(14,10);
            coveredByPolygon.Add(14,11);
            coveredByPolygon.Add(14,12);
            coveredByPolygon.Add(14,13);
            coveredByPolygon.Add(14,14);
            Polygon remainingPolygon = new Polygon(new List<MeshNode>{new MeshNode(14,10), new MeshNode(15,10), new MeshNode(15,14), new MeshNode(14,14)});
            Rectangle area = new Rectangle(10,10,4,4);
            List<MeshNode> createdNodes = remainingPolygon.GetNodes();

            NavigationMesh mesh = NavigationMeshFactory.CreateInstanceFromBlockmap(blocked,
                                                                            new List<Polygon>{remainingPolygon},
                                                                            coveredByPolygon,
                                                                            1,
                                                                            area,
                                                                            createdNodes,
                                                                            false);
            mesh.CheckIntegrity();
            Debug.WriteLine("CreateInstanceFromBlockmapTest ended.");
            GameLogic.GetInstance().GetGameStateManager().UnloadGame();
        }
コード例 #15
0
ファイル: NavigationMesh.cs プロジェクト: bethge/GalaticChaos
 /// <summary>Returns the index bounds for those sectors that are covered
 ///  by given rectangular polygon.</summary>
 /// <returns>int[]{xmin, ymin, xmax, ymax}</returns>
 private int[] GetCoveredSectorIndexes(Polygon polyRect)
 {
     int[] result = new int[4];
     result[0] = ((int)polyRect.Xmin) / mSectorWidth;
     result[1] = ((int)polyRect.Ymin) / mSectorHeight;
     result[2] = ((int)polyRect.Xmax) / mSectorWidth;
     result[3] = ((int)polyRect.Ymax) / mSectorHeight;
     return result;
 }
コード例 #16
0
ファイル: Polygon.cs プロジェクト: bethge/GalaticChaos
 /// <summary>
 /// If existant, returns the first common edge to the given polygon, else null.
 /// Result is a List of two edges: first edge belongs to this polygon,
 /// second edge belongs to given polygon2. Both are equivalent, e.g. are the
 /// "one" common edge.
 /// </summary>
 /// <param name="polygon2"></param>
 /// <returns></returns>
 private List<Edge> GetFirstIncidentEdge(Polygon polygon2)
 {
     Debug.Assert(mEdges != null);
     Debug.Assert(polygon2.GetEdges() != null);
     Debug.Assert(IsConcave(mEdges));
     Debug.Assert(IsConcave(polygon2.mEdges));
     bool edgesFound = false;
     List<Edge> commonEdges = null;
     List<Edge> edges2 = polygon2.GetEdges();
     // find the first common edge of both:
     foreach (Edge edge in mEdges)
     {
         Debug.Assert(edge.mPolyOwners.Count == 1);
         foreach (Edge edge2 in edges2)
         {
             if (edge.RecursiveReverse(edge2))
             {
                 if (!edgesFound)
                 {
                     commonEdges = new List<Edge> { edge, edge2 };
                     edgesFound = true;
                     // return commonEdges;
                 }
                 else
                 {
                     Debug.Assert(IsConcave(mEdges));
                     Debug.Assert(IsConcave(polygon2.mEdges));
                     throw new Exception("Polygon.GetFirstIncidentEdges() found too many results!");
                 }
             }
         }
     }
     return commonEdges;
 }
コード例 #17
0
ファイル: NavigationMesh.cs プロジェクト: bethge/GalaticChaos
 private void RemovePolygon(Polygon toRemove)
 {
     int[] indexes = GetCoveredSectorIndexes(toRemove);
     for (int y = indexes[1]; y <= indexes[3]; y++)
         for (int x = indexes[0]; x <= indexes[2]; x++)
         {
             bool success = mSectors[x, y].Remove(toRemove);
             Debug.Assert(success);
             Debug.Assert(!mSectors[x, y].Contains(toRemove));
         }
 }
コード例 #18
0
ファイル: Polygon.cs プロジェクト: bethge/GalaticChaos
        public static Polygon CreateRectInstance(Rectangle shape, List<MeshNode> nodesToUse, List<Polygon> polygons)
        {
            MeshNode leftUpper = null;
            MeshNode rightUpper = null;
            MeshNode rightLower = null;
            MeshNode leftLower = null;

            foreach(MeshNode node in nodesToUse)
            {
            // ReSharper disable CompareOfFloatsByEqualityOperator
                // I expect all node coordinates to be rounding-error-free
                if (node.mVector.X == shape.X && node.mVector.Y == shape.Y)
                    leftUpper = node;
                if (node.mVector.X == shape.X+shape.Width && node.mVector.Y == shape.Y)
                    rightUpper = node;
                if (node.mVector.X == shape.X+shape.Width && node.mVector.Y == shape.Y+shape.Height)
                    rightLower = node;
                if (node.mVector.X == shape.X && node.mVector.Y == shape.Y+shape.Height)
                    leftLower = node;
            // ReSharper restore CompareOfFloatsByEqualityOperator
            }
            if (leftUpper == null)
            {
                leftUpper = new MeshNode(shape.X, shape.Y);
                nodesToUse.Add(leftUpper);
                foreach (Polygon polygon in polygons) polygon.TrySplit(leftUpper);
            }
            if (rightUpper == null)
            {
                rightUpper = new MeshNode(shape.X+shape.Width, shape.Y);
                nodesToUse.Add(rightUpper);
                foreach (Polygon polygon in polygons) polygon.TrySplit(rightUpper);
            }
            if (rightLower == null)
            {
                rightLower = new MeshNode(shape.X+shape.Width, shape.Y+shape.Height);
                nodesToUse.Add(rightLower);
                foreach (Polygon polygon in polygons) polygon.TrySplit(rightLower);
            }
            if (leftLower == null)
            {
                leftLower = new MeshNode(shape.X, shape.Y+shape.Height);
                nodesToUse.Add(leftLower);
                foreach (Polygon polygon in polygons) polygon.TrySplit(leftLower);
            }
            Polygon result = new Polygon(new List<MeshNode> {leftUpper, rightUpper, rightLower, leftLower});
            foreach (MeshNode node in nodesToUse) result.TrySplit(node);
            return result;
        }
コード例 #19
0
ファイル: PathFinder.cs プロジェクト: bethge/GalaticChaos
        /// <summary>
        /// This does the actual work for CalculatePath(). CalculatePath() did just
        /// determine all the parameters for it.
        /// </summary>
        private Path CalculatePath2(Vector2 end, MeshNode startMeshNode, List<MeshNode> firstNeighbours, Polygon endPolygon, List<MeshNode> endNodes, bool startPassable, bool endPassable)
        {
            Debug.Assert(mMesh != null, "There is no navigation mesh! Load a map and then call PathFinder.GetInstance().LoadMeshFromObstructions().");
            // uses A* algorithm
            // !! remember to distinguish between latest costs (costs up to that point)
            // !! and heuristic costs (estimated path costs over that point to target)
            // I use AStarNode instead of MeshNode / Vector2 because I have to save
            // additional information, like predecessor and latest costs.
            // WorkingQueue should be in ascending order
            // of heuristic value. I use my own comparer to allow multiple
            // equal keys (since we may have nodes with the same heuristic value).
            MinHeap<AStarNode> workingQueue = new MinHeap<AStarNode>();
            // workingQueue doesn't support a quick check if some coords are
            // already contained, so we handle that with a corresp. structure:
            PointSet<float> workingCoords = new PointSet<float>();
            PointSet<float> exploredCoords = new PointSet<float>();

            // TODO: Evaluate First Loop iteration separately
            double startCostsToEnd = HelperMethods.EuklidDistance(startMeshNode.mVector, end);
            AStarNode current = new AStarNode(0, startCostsToEnd, startMeshNode, null);
            if (EndConditionMet(current, endPassable, endPolygon, endNodes))
                return HandleEnding(current, end, startPassable, endPassable, endPolygon);
            exploredCoords.Add(startMeshNode.mVector.X, startMeshNode.mVector.Y);
            HandleNeighbours(current, firstNeighbours, workingQueue, workingCoords, exploredCoords, end);
            // TODO: First iteration evaluation finished

            while (workingQueue.Count > 0)
            {
                //Debug.WriteLine("No. of nodes in the workingQueue: "+workingQueue.Count);
                //OutputHeuristics(workingQueue);
                current = GetNextNodeFromWorkingQueue(workingQueue, workingCoords);

                //Debug.WriteLine("Now examining "+current.mNode.mVector.X+","+current.mNode.mVector.Y);
                if (EndConditionMet(current, endPassable, endPolygon, endNodes))
                    return HandleEnding(current, end, startPassable, endPassable, endPolygon);

                // The shortest path to current has been found,
                // so we won't have to touch it ever again!
                exploredCoords.Add(current.mNode.mVector.X, current.mNode.mVector.Y);
                //Debug.WriteLine("Explored " + current.mNode.mVector.X + "," + current.mNode.mVector.Y);

                // Add all unexplored unblocked neighbours to workingQueue:
                List<MeshNode> neighbours = GetNeighbours2(current.mNode);
                HandleNeighbours(current, neighbours, workingQueue, workingCoords, exploredCoords, end);
            }

            // Working queue got empty, but we still didn't reach our target
            // -> No path found
            Debug.WriteLine("WARNING - Pathfinder: No path found!");
            return null;
        }
コード例 #20
0
        /// <summary>
        /// For code testing purposes:
        /// Creates a raster of polygons where each one is rectangle-shaped and adjacent to each other.
        /// Note that you have to pass x as countX for x columns of squares,
        /// the same applies in y direction. The returned mesh then accepts points in the range of
        /// (0,0)-(cellWidth*countX, cellHeight*countY).
        /// </summary>
        /// <param name="cellWidth">the width of each polygon</param>
        /// <param name="cellHeight">the height of each polygon</param>
        /// <param name="countX">the number of polygons to create in horizontal direction</param>
        /// <param name="countY">the number of polygons to create in vertical direction</param>
        /// <param name="doUnite">if the polygons should be united where possible (reduces raster complexity)</param>
        /// <returns>the new mesh instance</returns>
        public static NavigationMesh[] GetTestInstance(int cellWidth, int cellHeight, int countX, int countY, bool doUnite)
        {
            Debug.Assert(countX >= 1 && countY >= 1 && cellWidth > 0 && cellHeight > 0);
            NavigationMesh[] testMesh = new NavigationMesh[3];
            for (int i = 0; i < 3; i++)
            {
                testMesh[i] = new NavigationMesh(countX * cellWidth, countY * cellHeight);

                MeshNode[,] nodes = new MeshNode[countX + 1, countY + 1];
                for (int x = 0; x <= countX; x++)
                {
                    for (int y = 0; y <= countY; y++)
                    {
                        nodes[x, y] = new MeshNode(x * cellWidth, y * cellHeight);
                    }
                }
                for (int x = 0; x < countX; x++)
                {
                    for (int y = 0; y < countY; y++)
                    {
                        List<Edge> edges = new List<Edge>
                                           {
                                               new Edge(nodes[x, y], nodes[x + 1, y]),
                                               new Edge(nodes[x + 1, y], nodes[x + 1, y + 1]),
                                               new Edge(nodes[x + 1, y + 1], nodes[x, y + 1]),
                                               new Edge(nodes[x, y + 1], nodes[x, y])
                                           };
                        foreach (Edge edge in edges)
                        {
                            edge.ImposeOwnership();
                        }
                        Polygon polygon = new Polygon(edges);
                        polygon.ImposeOwnership();
                        testMesh[i].AddPolygon(polygon);
                    }
                }
                if (doUnite)
                {
                    testMesh[i].WholeUnionRun(true, null);
                }
            }

            return testMesh;
        }
コード例 #21
0
ファイル: PathFinder.cs プロジェクト: bethge/GalaticChaos
        /// <summary>
        /// Prepares the result for CalculatePath2().
        /// </summary>
        private Path HandleEnding(AStarNode current, Vector2 end, bool startPassable, bool endPassable, Polygon endPolygon)
        {
            // we're done!
            AStarNode last;
            if (endPassable && !current.mNode.mVector.Equals(end))
                // We really want to get to the end point!
                last = new AStarNode(0, 0, new MeshNode(end), current);
            else
                // We don't need to reach the end point
                last = current;

            // path cleanup needed?
            if (GlobalValues.GetInstance().mCleanUpPath)
                return PathCleaner.GetCleanedUpVectorPath(last, startPassable, endPolygon);
            /*else*/
                return GetPath(last, startPassable);
        }
コード例 #22
0
ファイル: PathCleaner.cs プロジェクト: bethge/GalaticChaos
 private static List<Polygon> GetCleanUpEndPolygons(int endIndex, List<MeshNode> meshNodes, Polygon endPolygon)
 {
     List<Polygon> result;
     if (endIndex == meshNodes.Count - 1)
     {
         result = new List<Polygon> { endPolygon };
     }
     else
     {
         result = meshNodes[endIndex].mPolyOwners;
     }
     return result;
 }