Ejemplo n.º 1
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);

        // Continue until no more inner polygons to process
        while (todo.Count > 0)
        {
            // Find the inner polygon with the highest x vertex
            LinkedListNode <EarClipPolygon> found      = null;
            LinkedListNode <EarClipVertex>  foundstart = null;
            LinkedListNode <EarClipPolygon> ip         = todo.First;
            while (ip != null)
            {
                LinkedListNode <EarClipVertex> 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 todo 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();
    }
Ejemplo n.º 2
0
    // This cuts into outer polygons to solve inner polygons and make the polygon tree flat
    private 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);
            }
        }
    }
    // 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);
    }
 // This merges a polygon into this one
 public void Add(EarClipPolygon p)
 {
     // Initialize
     foreach (EarClipVertex v in p)
     {
         base.AddLast(v);
     }
 }
Ejemplo n.º 5
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);
    }
    // This makes a polygon from the path
    public EarClipPolygon MakePolygon()
    {
        EarClipPolygon p = new EarClipPolygon();

        // Any sides at all?
        if (base.Count > 0)
        {
            // Add all sides
            for (int i = 0; i < base.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);
    }
Ejemplo n.º 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> n2;

        EarClipVertex[] t;
        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
        LinkedListNode <EarClipVertex> 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
            EarClipVertex 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();
            EarClipVertex v1 = t[0];
            EarClipVertex v2 = t[2];

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

            // Test first neighbour
            EarClipVertex[] t1 = GetTriangle(v1);
            //bool t1a = true;	//TriangleHasArea(t1);
            if (/*t1a && */ 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
            EarClipVertex[] t2 = GetTriangle(v2);
            //bool t2a = true;	//TriangleHasArea(t2);
            if (/*t2a && */ 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 && (/*!t1a || */ CheckValidEar(t1, reflexes)))
            {
                v1.AddEarTip(eartips);
            }
            else
            {
                v1.RemoveEarTip();
            }
            if (!v2.IsReflex && (/*!t2a || */ 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);
    }
Ejemplo n.º 8
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);
    }
Ejemplo n.º 9
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    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);
                float  u, ul;
                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));
            }
        }
    }
 // Constructor
 internal EarClipPolygon(EarClipPolygon p, EarClipVertex add) : base(p)
 {
     // Initialize
     base.AddLast(add);
     children = new List <EarClipPolygon>();
 }