Example #1
0
        /// <summary>
        /// Split up a Polygon with the Diagonals.
        /// This function splits the Polygon into two SubPolygons using the first Diagonal
        /// and then calls itself recursively.
        /// </summary>
        /// <param name="poly">The Polygon to split</param>
        /// <param name="diagonals">The Diagonals inside the Polygon, that are used to split it</param>
        /// <returns>The SubPolygons that were created by splitting the input Polygon</returns>
        private static PolygonData[] SplitPolygonWithDiagonals(PolygonData poly, List <Tuple <int, int> > diagonals)
        {
            // Count of the SubPolygons to create
            var subPolygonCount = diagonals.Count + 1;

            if (subPolygonCount == 1)
            {
                return new PolygonData[] { poly }
            }
            ;
            var subPolygons = new List <PolygonData>();
            // Subdivide with with first Diagonal
            var diagonal = diagonals[0];
            // Create the first SubPolygon
            var firstSubPolygonPoints = poly.Points.TakeWhile(p => p.Index != diagonal.Item1).ToList();

            firstSubPolygonPoints.Add(poly.Points.First(p => p.Index == diagonal.Item1));
            firstSubPolygonPoints.AddRange(poly.Points.SkipWhile(p => p.Index != diagonal.Item2).ToList());
            // HashSet of the Indices of the first SubPolygon
            // Used to split the remaining Diagonals
            var firstSubPolygonIndices = new HashSet <int>(firstSubPolygonPoints.Select(p => p.Index));
            // Create the second SubPolygon
            var secondSubPolygonPoints = poly.Points.SkipWhile(p => p.Index != diagonal.Item1).ToList();

            secondSubPolygonPoints = secondSubPolygonPoints.TakeWhile(p => p.Index != diagonal.Item2).ToList();
            secondSubPolygonPoints.Add(poly.Points.First(p => p.Index == diagonal.Item2));
            // We don't need this Diagonal any more
            diagonals.Remove(diagonal);
            // Split the Diagonals into two sets of Diagonals
            // Depending on in which SupPolygon it is located
            var diagsFirst  = new List <Tuple <int, int> >();
            var diagsSecond = new List <Tuple <int, int> >();

            foreach (var diag in diagonals)
            {
                if (firstSubPolygonIndices.Contains(diag.Item1) && firstSubPolygonIndices.Contains(diag.Item2))
                {
                    diagsFirst.Add(diag);
                }
                else
                {
                    diagsSecond.Add(diag);
                }
            }

            // Calculate the Diagonals that are positioned in first and second Subpolygon
            subPolygons.AddRange(SplitPolygonWithDiagonals(new PolygonData(firstSubPolygonPoints), diagsFirst).ToList());

            // Recursively call Function for first Subpolygon and second Subpolygon
            subPolygons.AddRange(SplitPolygonWithDiagonals(new PolygonData(secondSubPolygonPoints), diagsSecond).ToList());

            // Return the split-up Polygons
            return(subPolygons.ToArray());
        }
Example #2
0
        /// <summary>
        /// Perform the Triangulation of the Input.
        /// </summary>
        /// <param name="polygon">The Input Polygon</param>
        /// <param name="holes">The Input Polygon</param>
        /// <returns>List of Indices representing the Triangulation of the Polygon</returns>
        public static Int32Collection Triangulate(IList <Point> polygon, List <List <Point> > holes = null)
        {
            // Allocate and initialize List of Indices in Polygon
            var result = new Int32Collection();

            // Point-List from Input
            // (we don't want the first and last Point to be present two times)
            var points = polygon.ToList();

            if (points[0] == points[points.Count - 1])
            {
                points.RemoveAt(points.Count - 1);
            }
            var count = points.Count;

            // Sort the Input and create the Datastructures
            // Make the Polygon CounterClockWise
            var didReverse = false;

            if (!IsCCW(polygon))
            {
                points.Reverse();
                didReverse = true;
            }

            // Skip Polygons that don't need Triangulation
            if (count < 3)
            {
                return(null);
            }
            else if (count == 3)
            {
                if (!didReverse)
                {
                    return(new Int32Collection {
                        0, 1, 2
                    });
                }
                else
                {
                    return(new Int32Collection {
                        2, 1, 1
                    });
                }
            }

            var poly = new PolygonData(points);

            if (holes != null)
            {
                foreach (var hole in holes)
                {
                    poly.AddHole(hole);
                }
            }
            // Sort Points from highest y to lowest y
            // and if two or more Points have the same y Value from lowest x to highest x Value
            var events = new List <PolygonPoint>(poly.Points);

            events.Sort();

            // Calculate the Diagonals in the Down Sweep
            var diagonals = CalculateDiagonals(events);

            // Reverse the Order of the Events
            events.Reverse();
            // Add the Diagonals in the Up Sweep (and remove duplicates)
            diagonals.AddRange(CalculateDiagonals(events, false));
            diagonals = diagonals.Distinct().ToList();

            // Use Diagonals to split into nonotone Polygons
            var monotonePolygons = SplitIntoPolygons(poly, diagonals);

            // y-Monotone Polygons
            // Triangulate
            foreach (var monoton in monotonePolygons.Where(m => m != null))
            {
                var indices = TriangulateMonotone(monoton);
                foreach (var index in indices)
                {
                    result.Add(index);
                }
            }

            // If we reversed the Polygon,
            // we need to reverse the result also to get a correct Triangulation
            if (didReverse)
            {
                // Transform back every calculated Index
                for (int i = 0; i < result.Count; i++)
                {
                    result[i] = count - result[i] - 1;
                }
            }

            // Return all calculated Triangleindices
            return(result);
        }
Example #3
0
        /// <summary>
        /// Split Polygon into subpolagons using the calculated Diagonals
        /// </summary>
        /// <param name="poly">The Base-Polygon</param>
        /// <param name="diagonals">The Split-Diagonals</param>
        /// <returns>List of Subpolygons</returns>
        private static List <PolygonData> SplitIntoPolygons(PolygonData poly, List <Tuple <int, int> > diagonals)
        {
            if (diagonals.Count == 0)
            {
                return new List <PolygonData>()
                       {
                           poly
                       }
            }
            ;

            diagonals = diagonals.OrderBy(d => d.Item1).ThenBy(d => d.Item2).ToList();
            var edges = new SortedDictionary <int, List <PolygonEdge> >();

            foreach (var edge in poly.Points.Select(p => p.EdgeTwo)
                     .Union(diagonals.Select(d => new PolygonEdge(poly.Points[d.Item1], poly.Points[d.Item2])))
                     .Union(diagonals.Select(d => new PolygonEdge(poly.Points[d.Item2], poly.Points[d.Item1]))))
            {
                if (!edges.ContainsKey(edge.PointOne.Index))
                {
                    edges.Add(edge.PointOne.Index, new List <PolygonEdge>()
                    {
                        edge
                    });
                }
                else
                {
                    edges[edge.PointOne.Index].Add(edge);
                }
            }

            var subPolygons = new List <PolygonData>();

            var cnt = 0;

            foreach (var edge in edges)
            {
                cnt += edge.Value.Count;
            }

            // For each Diagonal
            while (edges.Count > 0)
            {
                // Start at first Diagonal Point
                var currentPoint     = edges.First().Value.First().PointOne;
                var nextEdge         = new PolygonEdge(null, null);
                var subPolygonPoints = new List <PolygonPoint>();
                // March along the edges to form a monotone Polygon
                // Until the current Point equals the StartPoint
                do
                {
                    // Add the current Point
                    subPolygonPoints.Add(currentPoint);
                    // Select the next Edge
                    var possibleEdges = edges[currentPoint.Index].ToList();
                    nextEdge = BestEdge(currentPoint, nextEdge, possibleEdges);
                    // Remove Edge from possible Edges
                    edges[currentPoint.Index].Remove(nextEdge);
                    if (edges[currentPoint.Index].Count == 0)
                    {
                        edges.Remove(currentPoint.Index);
                    }

                    // Move to the next Point
                    currentPoint = nextEdge.PointTwo;
                }while (subPolygonPoints[0].Index != currentPoint.Index);
                // Add the new SubPolygon
                subPolygons.Add(new PolygonData(subPolygonPoints));
            }

            return(subPolygons);
        }
Example #4
0
        /// <summary>
        /// Triangulate the y-Monotone Polygons.
        /// </summary>
        /// <param name="monoton">The y-Monotone Polygon to triangle</param>
        /// <returns>Index-List of Polygon Points (Indices from the original Polygon)</returns>
        private static Int32Collection TriangulateMonotone(PolygonData monoton)
        {
            // Collection to return
            Int32Collection result = new Int32Collection();

            // Sort the Events
            var events = new List <PolygonPoint>(monoton.Points);

            events.Sort();

            // Stack of Events to push to and pop from
            var pointStack = new Stack <PolygonPoint>();

            // Push the first two Events
            pointStack.Push(events[0]);
            pointStack.Push(events[1]);

            // Left- and right Chain for Triangulation
            var left  = (events[0].Next == events[1]) ? events[1] : events[0];
            var right = (events[0].Last == events[1]) ? events[1] : events[0];

            // Count of Points
            var pointCnt = monoton.Points.Count;

            // Handle the 3rd...n-th Point to triangle
            for (int i = 2; i < pointCnt; i++)
            {
                // The current Point
                var newPoint = events[i];
                var top      = pointStack.Peek();
                // If the new Point is not on the same side as the last Point on the Stack
                //if (!(leftChain.Contains(top) && leftChain.Contains(newPoint) || rightChain.Contains(top) && rightChain.Contains(newPoint)))
                if (!(top.Last == newPoint || top.Next == newPoint))
                {
                    // Determine this Point's Chain (left or right)
                    if (left.Next == newPoint)
                    {
                        left = newPoint;
                    }
                    else if (right.Last == newPoint)
                    {
                        right = newPoint;
                    }

                    // Third triangle Point
                    var p2 = top;
                    // While there is a Point on the Stack
                    while (pointStack.Count != 0)
                    {
                        // Pop and set the third Point
                        top = pointStack.Pop();
                        p2  = top;
                        if (pointStack.Count != 0)
                        {
                            // Pop again
                            top = pointStack.Pop();

                            // Add to the result. The Order is depending on the Side
                            if (left == newPoint)
                            {
                                result.Add(newPoint.Index);
                                result.Add(p2.Index);
                                result.Add(top.Index);
                            }
                            else
                            {
                                result.Add(newPoint.Index);
                                result.Add(top.Index);
                                result.Add(p2.Index);
                            }
                        }
                        // If more Points are on the Stack,
                        // Push the Point back again, to be able to form the Triangles
                        if (pointStack.Count != 0)
                        {
                            pointStack.Push(top);
                        }
                    }
                    // Push the last to Points on the Stack
                    pointStack.Push(events[i - 1]);
                    pointStack.Push(newPoint);
                }
                // If the newPoint is on the same Side (i.e. Chain)
                else
                {
                    // Get to Point on the Stack
                    top = pointStack.Pop();
                    var p2 = top;

                    // Determine this Point's Chain (left or right)
                    if (left.Next == newPoint && right.Last == newPoint)
                    {
                        if (top.Last == newPoint)
                        {
                            right = newPoint;
                        }
                        else if (top.Next == newPoint)
                        {
                            left = newPoint;
                        }
                        else
                        {
                            throw new Exception("Triangulation error");
                        }
                    }
                    else if (left.Next == newPoint)
                    {
                        left = newPoint;
                    }
                    else if (right.Last == newPoint)
                    {
                        right = newPoint;
                    }

                    while (pointStack.Count != 0)
                    {
                        // If the Triangle is possible, add it to the result (Point Order depends on the Side)
                        if (right == newPoint && IsCCW(new List <Point> {
                            newPoint.Point, p2.Point, pointStack.Peek().Point
                        }))
                        {
                            top = pointStack.Pop();
                            result.Add(newPoint.Index);
                            result.Add(p2.Index);
                            result.Add(top.Index);
                            p2 = top;
                        }
                        else if (left == newPoint && !IsCCW(new List <Point> {
                            newPoint.Point, p2.Point, pointStack.Peek().Point
                        }))
                        {
                            top = pointStack.Pop();
                            result.Add(newPoint.Index);
                            result.Add(top.Index);
                            result.Add(p2.Index);
                            p2 = top;
                        }
                        // No Triangle possible, just leave the Loop
                        else
                        {
                            break;
                        }
                    }
                    // Push the last two Points on the Stack
                    pointStack.Push(p2);
                    pointStack.Push(newPoint);
                }
            }
            // Return the Triangulation
            return(result);
        }
Example #5
0
        /// <summary>
        /// Perform the Triangulation of the Input.
        /// </summary>
        /// <param name="polygon">The Input Polygon</param>
        /// <param name="holes">The Input Polygon</param>
        /// <returns>List of Indices representing the Triangulation of the Polygon</returns>
        public static Int32Collection Triangulate(IList <Point> polygon, List <List <Point> > holes = null)
        {
            // Allocate and initialize List of Indices in Polygon
            var result = new Int32Collection();

            // Point-List from Input
            // (we don't want the first and last Point to be present two times)
            var points = polygon.ToList();

            if (points[0] == points[points.Count - 1])
            {
                points.RemoveAt(points.Count - 1);
            }
            var count = points.Count;

            // Sort the Input and create the Datastructures
            // Make the Polygon CounterClockWise
            var didReverse = false;

            if (!isCCW(polygon))
            {
                points.Reverse();
                didReverse = true;
            }

            // Skip Polygons that don't need Triangulation
            if (count < 3)
            {
                return(null);
            }
            else if (count == 3)
            {
                if (!didReverse)
                {
                    return(new Int32Collection {
                        0, 1, 2
                    });
                }
                else
                {
                    return(new Int32Collection {
                        2, 1, 1
                    });
                }
            }

            // Create Polygon Data Structure
            var poly = new PolygonData(points);

            // Sort Points from highest y to lowest y
            // and if two or more Points have the same y Value from lowest x to highest x Value
            var events = new List <PolygonPoint>(poly.Points);

            events.Sort();

            // Mapping from the main Polygon to the current Polygon
            var mainMapping = new Dictionary <int, int>();

            for (int i = 0; i < count; i++)
            {
                mainMapping[i] = i;
            }

            // Calculate the Diagonals in the Down Sweep
            var diagonals = CalculateDiagonals(events, mainMapping);

            // Split the Polygon
            var subPolygons = SplitPolygonWithDiagonals(poly, diagonals);

            var monotonePolygons = new List <PolygonData>();

            // Up Sweep for all Sub-Polygons
            foreach (var subPoly in subPolygons)
            {
                // Sort the Events from Bottom to Top
                events = new List <PolygonPoint>(subPoly.Points);
                events.Sort();
                events.Reverse();

                // Mapping from the main Polygon to the current Polygon
                var polyMapping = new Dictionary <int, int>();
                for (int i = 0; i < subPoly.Points.Count; i++)
                {
                    polyMapping[subPoly.Points[i].Index] = i;
                }
                // Calculate the Diagonals in the Up Sweep
                diagonals = CalculateDiagonals(events, polyMapping, false);

                // Split the Polygon
                var monotoneSubPolygons = SplitPolygonWithDiagonals(subPoly, diagonals);
                // Add to List of monotone Polygons
                monotonePolygons.AddRange(monotoneSubPolygons);
            }

            // y-Monotone Polygons
            // Triangulate
            foreach (var monoton in monotonePolygons)
            {
                var indices = TriangulateMonotone(monoton);
                foreach (var index in indices)
                {
                    result.Add(index);
                }
            }

            // If we reversed the Polygon,
            // we need to reverse the result also to get a correct Triangulation
            if (didReverse)
            {
                // Transform back every calculated Index
                for (int i = 0; i < result.Count; i++)
                {
                    result[i] = count - result[i] - 1;
                }
            }

            // Return all calculated Triangleindices
            return(result);
        }