using System; using System.Collections.Generic; namespace FarseerPhysics.Common.Decomposition.Seidel { internal class Triangulator { // Trapezoid decomposition list public List Trapezoids; public List> Triangles; // Initialize trapezoidal map and query structure private Trapezoid _boundingBox; private List _edgeList; private QueryGraph _queryGraph; private float _sheer = 0.001f; private TrapezoidalMap _trapezoidalMap; private List _xMonoPoly; public Triangulator(List polyLine, float sheer) { _sheer = sheer; Triangles = new List>(); Trapezoids = new List(); _xMonoPoly = new List(); _edgeList = InitEdges(polyLine); _trapezoidalMap = new TrapezoidalMap(); _boundingBox = _trapezoidalMap.BoundingBox(_edgeList); _queryGraph = new QueryGraph(Sink.Isink(_boundingBox)); Process(); } // Build the trapezoidal map and query graph private void Process() { foreach (Edge edge in _edgeList) { List traps = _queryGraph.FollowEdge(edge); // Remove trapezoids from trapezoidal Map foreach (Trapezoid t in traps) { _trapezoidalMap.Map.Remove(t); bool cp = t.Contains(edge.P); bool cq = t.Contains(edge.Q); Trapezoid[] tList; if (cp && cq) { tList = _trapezoidalMap.Case1(t, edge); _queryGraph.Case1(t.Sink, edge, tList); } else if (cp && !cq) { tList = _trapezoidalMap.Case2(t, edge); _queryGraph.Case2(t.Sink, edge, tList); } else if (!cp && !cq) { tList = _trapezoidalMap.Case3(t, edge); _queryGraph.Case3(t.Sink, edge, tList); } else { tList = _trapezoidalMap.Case4(t, edge); _queryGraph.Case4(t.Sink, edge, tList); } // Add new trapezoids to map foreach (Trapezoid y in tList) { _trapezoidalMap.Map.Add(y); } } _trapezoidalMap.Clear(); } // Mark outside trapezoids foreach (Trapezoid t in _trapezoidalMap.Map) { MarkOutside(t); } // Collect interior trapezoids foreach (Trapezoid t in _trapezoidalMap.Map) { if (t.Inside) { Trapezoids.Add(t); t.AddPoints(); } } // Generate the triangles CreateMountains(); } // Build a list of x-monotone mountains private void CreateMountains() { foreach (Edge edge in _edgeList) { if (edge.MPoints.Count > 2) { MonotoneMountain mountain = new MonotoneMountain(); // Sorting is a perfromance hit. Literature says this can be accomplised in // linear time, although I don't see a way around using traditional methods // when using a randomized incremental algorithm // Insertion sort is one of the fastest algorithms for sorting arrays containing // fewer than ten elements, or for lists that are already mostly sorted. List points = new List(edge.MPoints); points.Sort((p1, p2) => p1.X.CompareTo(p2.X)); foreach (Point p in points) mountain.Add(p); // Triangulate monotone mountain mountain.Process(); // Extract the triangles into a single list foreach (List t in mountain.Triangles) { Triangles.Add(t); } _xMonoPoly.Add(mountain); } } } // Mark the outside trapezoids surrounding the polygon private void MarkOutside(Trapezoid t) { if (t.Top == _boundingBox.Top || t.Bottom == _boundingBox.Bottom) t.TrimNeighbors(); } // Create segments and connect end points; update edge event pointer private List InitEdges(List points) { List edges = new List(); for (int i = 0; i < points.Count - 1; i++) { edges.Add(new Edge(points[i], points[i + 1])); } edges.Add(new Edge(points[0], points[points.Count - 1])); return OrderSegments(edges); } private List OrderSegments(List edgeInput) { // Ignore vertical segments! List edges = new List(); foreach (Edge e in edgeInput) { Point p = ShearTransform(e.P); Point q = ShearTransform(e.Q); // Point p must be to the left of point q if (p.X > q.X) { edges.Add(new Edge(q, p)); } else if (p.X < q.X) { edges.Add(new Edge(p, q)); } } // Randomized triangulation improves performance // See Seidel's paper, or O'Rourke's book, p. 57 Shuffle(edges); return edges; } private static void Shuffle(IList list) { Random rng = new Random(); int n = list.Count; while (n > 1) { n--; int k = rng.Next(n + 1); T value = list[k]; list[k] = list[n]; list[n] = value; } } // Prevents any two distinct endpoints from lying on a common vertical line, and avoiding // the degenerate case. See Mark de Berg et al, Chapter 6.3 private Point ShearTransform(Point point) { return new Point(point.X + _sheer * point.Y, point.Y); } } }