//Check if a vertex is an ear /// <summary> /// Checks if a vertex is an ear and sets its IsEar property accordingly. /// </summary> /// <param name="v"></param> /// <param name="vertices"></param> private static void SetIfVertexIsEar(Vertex v, IEnumerable <Vertex> vertices) { v.IsEar = false; //A concave/reflex vertex can't be an ear! if (v.IsConcave) { return; } // vertex positions that make up the triangle this vertex belongs to. var v1 = v.PreviousVertex.GetPos2D(); var v2 = v.GetPos2D(); var v3 = v.NextVertex.GetPos2D(); //if none of the concave vertices is inside the area of the triangle this vertex belongs to, this vertex is an ear. var anyConcaveVertexInsideTriangleArea = (from vertex in vertices where vertex.IsConcave select vertex.GetPos2D()).Any(point => GeometryHelper.PointInsideTriangleArea(v1, v2, v3, point)); if (!anyConcaveVertexInsideTriangleArea) { v.IsEar = true; } }
/// <summary> /// Triangulates a convex or concave polygon. /// The points on the polygon should be ordered counter-clockwise. /// This algorithm is called ear clipping and it's O(n*n) Another common algorithm is dividing it into trapezoids and it's O(n log n). /// </summary> /// <param name="vertices">Ordered list of vertices making up the polygon. Can be oriented CW as well as CCW.</param> /// <returns></returns> /// <exception cref="ArgumentException">If fewer than 3 vertices are provided.</exception> public static List <Triangle> TriangulateConcaveOrConvexPolygon(List <Vertex> vertices) { if (vertices.Count < 3) { throw new ArgumentException($"A polygon needs at least 3 vertices. Vertices provided: {vertices.Count}"); } //Rest of functions needs orientation to be ccw. Change orientation if it's cw if (GeometryHelper.PolygonOrientedClockwise(vertices)) { vertices.Reverse(); } var triangles = new List <Triangle>(); //If we just have three points, we can just return a single triangle if (vertices.Count == 3) { triangles.Add(new Triangle(vertices[0], vertices[2], vertices[1])); //vertices are expected to be in ccw order but Unity draw order for triangles in meshes is cw, hence 0,2,1 and not 0,1,2 return(triangles); } //Step 1. Set the next and prev vertex for every vertex //Find the next and previous vertex for (var i = 0; i < vertices.Count; i++) { var nextPos = CollectionsHelper.WrapIndex(i + 1, vertices.Count); var prevPos = CollectionsHelper.WrapIndex(i - 1, vertices.Count); vertices[i].PreviousVertex = vertices[prevPos]; vertices[i].NextVertex = vertices[nextPos]; } //Step 2. Find the reflex (concave) and convex vertices, and ear vertices foreach (var v in vertices) { SetIfVertexIsConcaveOrConvex(v); } //Have to find the ears after we have found if the vertex is concave or convex var earVertices = new List <Vertex>(); foreach (var v in vertices) { SetIfVertexIsEar(v, vertices); if (v.IsEar) { earVertices.Add(v); } } //Step 3. Triangulate! while (true) { //This means we have just one triangle left if (vertices.Count == 3) { //The final triangle triangles.Add(new Triangle(vertices[0], vertices[0].PreviousVertex, vertices[0].NextVertex)); break; } //Make a triangle of the first ear var earVertex = earVertices[0]; var earVertexPrev = earVertex.PreviousVertex; var earVertexNext = earVertex.NextVertex; var newTriangle = new Triangle(earVertex, earVertexPrev, earVertexNext); triangles.Add(newTriangle); //Remove the vertex from the lists earVertices.Remove(earVertex); vertices.Remove(earVertex); //Update the previous vertex and next vertex so that they are now directly linked together (take current ear vertex out of doubly linked list) earVertexPrev.NextVertex = earVertexNext; earVertexNext.PreviousVertex = earVertexPrev; //...see if we have found a new ear by investigating the two vertices that was part of the ear and add them to the list of ears, if that's the case. SetIfVertexIsConcaveOrConvex(earVertexPrev); SetIfVertexIsConcaveOrConvex(earVertexNext); earVertices.Remove(earVertexPrev); earVertices.Remove(earVertexNext); SetIfVertexIsEar(earVertexPrev, vertices); if (earVertexPrev.IsEar) { earVertices.Add(earVertexPrev); } SetIfVertexIsEar(earVertexNext, vertices); if (earVertexNext.IsEar) { earVertices.Add(earVertexNext); } } return(triangles); }
/// <summary> /// Checks if a vertex is concave or convex and sets its IsConcave property accordingly- /// </summary> /// <param name="v"></param> private static void SetIfVertexIsConcaveOrConvex(Vertex v) { //This is a concave/reflex vertex if its triangle is oriented clockwise v.IsConcave = GeometryHelper.TriangleOrientedClockwise(v.PreviousVertex.GetPos2D(), v.GetPos2D(), v.NextVertex.GetPos2D()); }