/// <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()); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }