コード例 #1
0
        // This makes a polygon from the path
        public EarClipPolygon MakePolygon(bool startfront)
        {
            EarClipPolygon p       = new EarClipPolygon();
            bool           forward = startfront;

            // Any sides at all?
            if (Count > 0)
            {
                p.AddLast(forward ? new EarClipVertex(base[0].Start.Position, base[0].Front) : new EarClipVertex(base[0].End.Position, base[0].Back));

                // Add all lines, but the first
                for (int i = 1; i < Count; i++)
                {
                    // Traverse direction changes?
                    if ((base[i - 1].Start == base[i].Start) ||
                        (base[i - 1].End == base[i].End))
                    {
                        forward = !forward;
                    }

                    // Add next vertex
                    p.AddLast(forward ? new EarClipVertex(base[i].Start.Position, base[i].Front) : new EarClipVertex(base[i].End.Position, base[i].Back));
                }
            }

            return(p);
        }
コード例 #2
0
        // This inserts a polygon if it is a child of this one
        public bool InsertChild(EarClipPolygon p)
        {
            // Polygon must have at least 1 vertex
            if (p.Count == 0)
            {
                return(false);
            }

            // Check if it can be inserted at a lower level
            foreach (EarClipPolygon child in children)
            {
                if (child.InsertChild(p))
                {
                    return(true);
                }
            }

            // Check if it can be inserted here
            if (this.Intersect(p.First.Value.Position))
            {
                // Make the polygon the inverse of this one
                p.Inner = !inner;
                children.Add(p);
                return(true);
            }

            // Can't insert it as a child
            return(false);
        }
コード例 #3
0
        // This cuts into outer polygons to solve inner polygons and make the polygon tree flat
        private static void DoCutting(List <EarClipPolygon> polys)
        {
            Queue <EarClipPolygon> todo = new Queue <EarClipPolygon>(polys);

            // Begin processing outer polygons
            while (todo.Count > 0)
            {
                // Get outer polygon to process
                EarClipPolygon p = todo.Dequeue();

                // Any inner polygons to work with?
                if (p.Children.Count > 0)
                {
                    // Go for all the children
                    foreach (EarClipPolygon c in p.Children)
                    {
                        // The children of the children are outer polygons again,
                        // so move them to the root and add for processing
                        polys.AddRange(c.Children);
                        foreach (EarClipPolygon sc in c.Children)
                        {
                            todo.Enqueue(sc);
                        }

                        // Remove from inner polygon
                        c.Children.Clear();
                    }

                    // Now do some cutting on this polygon to merge the inner polygons
                    MergeInnerPolys(p);
                }
            }
        }
コード例 #4
0
        // This finds the right-most vertex in an inner polygon to use for cut startpoint.
        private static LinkedListNode <EarClipVertex> FindRightMostVertex(EarClipPolygon p)
        {
            LinkedListNode <EarClipVertex> found = p.First;
            LinkedListNode <EarClipVertex> v     = found.Next;

            // Go for all vertices to find the on with the biggest x value
            while (v != null)
            {
                if (v.Value.Position.x > found.Value.Position.x)
                {
                    found = v;
                }
                v = v.Next;
            }

            // Return result
            return(found);
        }
コード例 #5
0
        // This takes an outer polygon and a set of inner polygons to start cutting on
        private static void MergeInnerPolys(EarClipPolygon p)
        {
            LinkedList <EarClipPolygon>     todo = new LinkedList <EarClipPolygon>(p.Children);
            LinkedListNode <EarClipVertex>  start;
            LinkedListNode <EarClipPolygon> ip;
            LinkedListNode <EarClipPolygon> found;
            LinkedListNode <EarClipVertex>  foundstart;

            // Continue until no more inner polygons to process
            while (todo.Count > 0)
            {
                // Find the inner polygon with the highest x vertex
                found      = null;
                foundstart = null;
                ip         = todo.First;
                while (ip != null)
                {
                    start = FindRightMostVertex(ip.Value);
                    if ((foundstart == null) || (start.Value.Position.x > foundstart.Value.Position.x))
                    {
                        // Found a better start
                        found      = ip;
                        foundstart = start;
                    }

                    // Next!
                    ip = ip.Next;
                }

                // Remove from to do list
                todo.Remove(found);

                // Get cut start and end
                SplitOuterWithInner(foundstart, p);
            }

            // Remove the children, they should be merged in the polygon by now
            p.Children.Clear();
        }
コード例 #6
0
        // This makes a polygon from the path
        public EarClipPolygon MakePolygon()
        {
            EarClipPolygon p = new EarClipPolygon();

            // Any sides at all?
            if (Count > 0)
            {
                // Add all sides
                for (int i = 0; i < Count; i++)
                {
                    // On front or back?
                    if (base[i].IsFront)
                    {
                        p.AddLast(new EarClipVertex(base[i].Line.End.Position, base[i]));
                    }
                    else
                    {
                        p.AddLast(new EarClipVertex(base[i].Line.Start.Position, base[i]));
                    }
                }
            }

            return(p);
        }
コード例 #7
0
        // This clips a polygon and returns the triangles
        // The polygon may not have any holes or islands
        // See: http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
        private int DoEarClip(EarClipPolygon poly, List <Vector2D> verticeslist, List <Sidedef> sidedefslist)
        {
            LinkedList <EarClipVertex>     verts = new LinkedList <EarClipVertex>();
            List <EarClipVertex>           convexes = new List <EarClipVertex>(poly.Count);
            LinkedList <EarClipVertex>     reflexes = new LinkedList <EarClipVertex>();
            LinkedList <EarClipVertex>     eartips = new LinkedList <EarClipVertex>();
            LinkedListNode <EarClipVertex> n1, n2;
            EarClipVertex v, v1, v2;

            EarClipVertex[] t, t1, t2;
            int             countvertices = 0;

            // Go for all vertices to fill list
            foreach (EarClipVertex vec in poly)
            {
                vec.SetVertsLink(verts.AddLast(vec));
            }

            // Remove any zero-length lines, these will give problems
            n1 = verts.First;
            do
            {
                // Continue until adjacent zero-length lines are removed
                n2 = n1.Next ?? verts.First;
                Vector2D d = n1.Value.Position - n2.Value.Position;
                while ((Math.Abs(d.x) < 0.00001f) && (Math.Abs(d.y) < 0.00001f))
                {
                    n2.Value.Remove();
                    n2 = n1.Next ?? verts.First;
                    if (n2 != null)
                    {
                        d = n1.Value.Position - n2.Value.Position;
                    }
                    else
                    {
                        break;
                    }
                }

                // Next!
                n1 = n2;
            }while(n1 != verts.First);

            // Optimization: Vertices which have lines with the
            // same angle are useless. Remove them!
            n1 = verts.First;
            while (n1 != null)
            {
                // Get the next vertex
                n2 = n1.Next;

                // Get triangle for v
                t = GetTriangle(n1.Value);

                // Check if both lines have the same angle
                Line2D a = new Line2D(t[0].Position, t[1].Position);
                Line2D b = new Line2D(t[1].Position, t[2].Position);
                if (Math.Abs(Angle2D.Difference(a.GetAngle(), b.GetAngle())) < 0.00001f)
                {
                    // Same angles, remove vertex
                    n1.Value.Remove();
                }

                // Next!
                n1 = n2;
            }

            // Go for all vertices to determine reflex or convex
            foreach (EarClipVertex vv in verts)
            {
                // Add to reflex or convex list
                if (IsReflex(GetTriangle(vv)))
                {
                    vv.AddReflex(reflexes);
                }
                else
                {
                    convexes.Add(vv);
                }
            }

            // Go for all convex vertices to see if they are ear tips
            foreach (EarClipVertex cv in convexes)
            {
                // Add when this is a valid ear
                t = GetTriangle(cv);
                if (CheckValidEar(t, reflexes))
                {
                    cv.AddEarTip(eartips);
                }
            }

                        #if DEBUG
            if (OnShowPolygon != null)
            {
                OnShowPolygon(verts);
            }
                        #endif

            // Process ears until done
            while ((eartips.Count > 0) && (verts.Count > 2))
            {
                // Get next ear
                v = eartips.First.Value;
                t = GetTriangle(v);

                // Only save this triangle when it has an area
                if (TriangleHasArea(t))
                {
                    // Add ear as triangle
                    AddTriangleToList(t, verticeslist, sidedefslist, (verts.Count == 3));
                    countvertices += 3;
                }

                // Remove this ear from all lists
                v.Remove();
                v1 = t[0];
                v2 = t[2];

                                #if DEBUG
                if (TriangleHasArea(t))
                {
                    if (OnShowEarClip != null)
                    {
                        OnShowEarClip(t, verts);
                    }
                }
                                #endif

                // Test first neighbour
                t1 = GetTriangle(v1);
                if (IsReflex(t1))
                {
                    // List as reflex if not listed yet
                    if (!v1.IsReflex)
                    {
                        v1.AddReflex(reflexes);
                    }
                    v1.RemoveEarTip();
                }
                else
                {
                    // Remove from reflexes
                    v1.RemoveReflex();
                }

                // Test second neighbour
                t2 = GetTriangle(v2);
                if (IsReflex(t2))
                {
                    // List as reflex if not listed yet
                    if (!v2.IsReflex)
                    {
                        v2.AddReflex(reflexes);
                    }
                    v2.RemoveEarTip();
                }
                else
                {
                    // Remove from reflexes
                    v2.RemoveReflex();
                }

                // Check if any neightbour have become a valid or invalid ear
                if (!v1.IsReflex && (CheckValidEar(t1, reflexes)))
                {
                    v1.AddEarTip(eartips);
                }
                else
                {
                    v1.RemoveEarTip();
                }
                if (!v2.IsReflex && (CheckValidEar(t2, reflexes)))
                {
                    v2.AddEarTip(eartips);
                }
                else
                {
                    v2.RemoveEarTip();
                }
            }

                        #if DEBUG
            if (OnShowRemaining != null)
            {
                OnShowRemaining(verts);
            }
                        #endif

            // Dispose remaining vertices
            foreach (EarClipVertex ecv in verts)
            {
                ecv.Dispose();
            }

            // Return the number of vertices in the result
            return(countvertices);
        }
コード例 #8
0
        // This finds the cut coordinates and splits the other poly with inner vertices
        private static void SplitOuterWithInner(LinkedListNode <EarClipVertex> start, EarClipPolygon p)
        {
            LinkedListNode <EarClipVertex> insertbefore = null;
            float    u, ul, foundu = float.MaxValue;
            Vector2D foundpos   = new Vector2D();

            // Create a line from start that goes beyond the right most vertex of p
            LinkedListNode <EarClipVertex> pr = FindRightMostVertex(p);
            float  startx       = start.Value.Position.x;
            float  endx         = pr.Value.Position.x + 10.0f;
            Line2D starttoright = new Line2D(start.Value.Position, new Vector2D(endx, start.Value.Position.y));

            // Calculate a small bonus (0.1 mappixel)
            float bonus = starttoright.GetNearestOnLine(new Vector2D(start.Value.Position.x + 0.1f, start.Value.Position.y));

            // Go for all lines in the outer polygon
            LinkedListNode <EarClipVertex> v1 = p.Last;
            LinkedListNode <EarClipVertex> v2 = p.First;

            while (v2 != null)
            {
                // Check if the line goes between startx and endx
                if (((v1.Value.Position.x > startx) ||
                     (v2.Value.Position.x > startx)) &&
                    ((v1.Value.Position.x < endx) ||
                     (v2.Value.Position.x < endx)))
                {
                    // Find intersection
                    Line2D pl = new Line2D(v1.Value.Position, v2.Value.Position);
                    pl.GetIntersection(starttoright, out u, out ul);
                    if (float.IsNaN(u))
                    {
                        // We have found a line that is perfectly horizontal
                        // (parallel to the cut scan line) Check if the line
                        // is overlapping the cut scan line.
                        if (v1.Value.Position.y == start.Value.Position.y)
                        {
                            // This is an exceptional situation which causes a bit of a problem, because
                            // this could be a previously made cut, which overlaps another line from the
                            // same cut and we have to determine which of the two we will join with. If we
                            // pick the wrong one, the polygon is no longer valid and triangulation will fail.

                            // Calculate distance of each vertex in units
                            u  = starttoright.GetNearestOnLine(v1.Value.Position);
                            ul = starttoright.GetNearestOnLine(v2.Value.Position);

                            // Rule out vertices before the scan line
                            if (u < 0.0f)
                            {
                                u = float.MaxValue;
                            }
                            if (ul < 0.0f)
                            {
                                ul = float.MaxValue;
                            }

                            float    insert_u = Math.Min(u, ul);
                            Vector2D inserpos = starttoright.GetCoordinatesAt(insert_u);

                            // Check in which direction the line goes.
                            if (v1.Value.Position.x > v2.Value.Position.x)
                            {
                                // The line goes from right to left (towards our start point)
                                // so we must always insert our cut after this line.

                                // If the next line goes up, we consider this a better candidate than
                                // a horizontal line that goes from left to right (the other cut line)
                                // so we give it a small bonus.
                                LinkedListNode <EarClipVertex> v3 = v2.Next ?? v2.List.First;
                                if (v3.Value.Position.y < v2.Value.Position.y)
                                {
                                    insert_u -= bonus;
                                }

                                // Remember this when it is a closer match
                                if (insert_u <= foundu)
                                {
                                    insertbefore = v2.Next ?? v2.List.First;
                                    foundu       = insert_u;
                                    foundpos     = inserpos;
                                }
                            }
                            else
                            {
                                // The line goes from left to right (away from our start point)
                                // so we must always insert our cut before this line.

                                // If the previous line goes down, we consider this a better candidate than
                                // a horizontal line that goes from right to left (the other cut line)
                                // so we give it a small bonus.
                                LinkedListNode <EarClipVertex> v3 = v1.Previous ?? v1.List.Last;
                                if (v3.Value.Position.y > v1.Value.Position.y)
                                {
                                    insert_u -= bonus;
                                }

                                // Remember this when it is a closer match
                                if (insert_u <= foundu)
                                {
                                    insertbefore = v2;
                                    foundu       = insert_u;
                                    foundpos     = inserpos;
                                }
                            }
                        }
                    }
                    // Found a closer match?
                    else if ((ul >= 0.0f) && (ul <= 1.0f) && (u > 0.0f) && (u <= foundu))
                    {
                        // Found a closer intersection
                        insertbefore = v2;
                        foundu       = u;
                        foundpos     = starttoright.GetCoordinatesAt(u);
                    }
                }

                // Next
                v1 = v2;
                v2 = v2.Next;
            }

            // Found anything?
            if (insertbefore != null)
            {
                Sidedef sd = (insertbefore.Previous == null) ? insertbefore.List.Last.Value.Sidedef : insertbefore.Previous.Value.Sidedef;

                // Find the position where we have to split the outer polygon
                EarClipVertex split = new EarClipVertex(foundpos, null);

                // Insert manual split vertices
                p.AddBefore(insertbefore, new EarClipVertex(split, sd));

                // Start inserting from the start (do I make sense this time?)
                v1 = start;
                do
                {
                    // Insert inner polygon vertex
                    p.AddBefore(insertbefore, new EarClipVertex(v1.Value));
                    v1 = (v1.Next ?? v1.List.First);
                } while(v1 != start);

                // Insert manual split vertices
                p.AddBefore(insertbefore, new EarClipVertex(start.Value, sd));
                if (split.Position != insertbefore.Value.Position)
                {
                    p.AddBefore(insertbefore, new EarClipVertex(split, sd));
                }
            }
        }
コード例 #9
0
        // This traces sector lines to create a polygon tree
        private static List <EarClipPolygon> DoTrace(Sector s)
        {
            Dictionary <Sidedef, bool>  todosides = new Dictionary <Sidedef, bool>(s.Sidedefs.Count);
            Dictionary <Vertex, Vertex> ignores   = new Dictionary <Vertex, Vertex>();
            List <EarClipPolygon>       root      = new List <EarClipPolygon>();

            // Fill the dictionary
            // The bool value is used to indicate lines which has been visited in the trace
            foreach (Sidedef sd in s.Sidedefs)
            {
                todosides.Add(sd, false);
            }

            // First remove all sides that refer to the same sector on both sides of the line
            RemoveDoubleSidedefReferences(todosides, s.Sidedefs);

            // Continue until all sidedefs have been processed
            while (todosides.Count > 0)
            {
                // Reset all visited indicators
                foreach (Sidedef sd in s.Sidedefs)
                {
                    if (todosides.ContainsKey(sd))
                    {
                        todosides[sd] = false;
                    }
                }

                // Find the right-most vertex to start a trace with.
                // This guarantees that we start out with an outer polygon and we just
                // have to check if it is inside a previously found polygon.
                Vertex start = FindRightMostVertex(todosides, ignores);

                // No more possible start vertex found?
                // Then leave with what we have up till now.
                if (start == null)
                {
                    break;
                }

                // Trace to find a polygon
                SidedefsTracePath path = DoTracePath(new SidedefsTracePath(), start, null, s, todosides);

                // If tracing is not possible (sector not closed?)
                // then add the start to the ignore list and try again later
                if (path == null)
                {
                    // Ignore vertex as start
                    ignores.Add(start, start);
                }
                else
                {
                    // Remove the sides found in the path
                    foreach (Sidedef sd in path)
                    {
                        todosides.Remove(sd);
                    }

                    // Create the polygon
                    EarClipPolygon newpoly = path.MakePolygon();

                    // Determine where this polygon goes in our tree
                    foreach (EarClipPolygon p in root)
                    {
                        // Insert if it belongs as a child
                        if (p.InsertChild(newpoly))
                        {
                            // Done
                            newpoly = null;
                            break;
                        }
                    }

                    // Still not inserted in our tree?
                    if (newpoly != null)
                    {
                        // Then add it at root level as outer polygon
                        newpoly.Inner = false;
                        root.Add(newpoly);
                    }
                }
            }

            // Return result
            return(root);
        }