/// <summary> /// Checks if vertex i is the tip of an ear in polygon defined by xv[] and yv[]. /// </summary> /// <param name="i">The i.</param> /// <param name="xv">The xv.</param> /// <param name="yv">The yv.</param> /// <param name="xvLength">Length of the xv.</param> /// <remarks> /// Assumes clockwise orientation of polygon. /// </remarks> /// <returns> /// <c>true</c> if the specified i is ear; otherwise, <c>false</c>. /// </returns> private static bool IsEar(int i, float[] xv, float[] yv, int xvLength) { float dx0, dy0, dx1, dy1; if (i >= xvLength || i < 0 || xvLength < 3) { return false; } int upper = i + 1; int lower = i - 1; if (i == 0) { dx0 = xv[0] - xv[xvLength - 1]; dy0 = yv[0] - yv[xvLength - 1]; dx1 = xv[1] - xv[0]; dy1 = yv[1] - yv[0]; lower = xvLength - 1; } else if (i == xvLength - 1) { dx0 = xv[i] - xv[i - 1]; dy0 = yv[i] - yv[i - 1]; dx1 = xv[0] - xv[i]; dy1 = yv[0] - yv[i]; upper = 0; } else { dx0 = xv[i] - xv[i - 1]; dy0 = yv[i] - yv[i - 1]; dx1 = xv[i + 1] - xv[i]; dy1 = yv[i + 1] - yv[i]; } float cross = dx0 * dy1 - dx1 * dy0; if (cross > 0) return false; Triangle myTri = new Triangle(xv[i], yv[i], xv[upper], yv[upper], xv[lower], yv[lower]); for (int j = 0; j < xvLength; ++j) { if (j == i || j == lower || j == upper) continue; if (myTri.IsInside(xv[j], yv[j])) return false; } return true; }
/// <summary> /// Triangulates a polygon using simple ear-clipping algorithm. Returns /// size of Triangle array unless the polygon can't be triangulated. /// This should only happen if the polygon self-intersects, /// though it will not _always_ return null for a bad polygon - it is the /// caller's responsibility to check for self-intersection, and if it /// doesn't, it should at least check that the return value is non-null /// before using. You're warned! /// /// Triangles may be degenerate, especially if you have identical points /// in the input to the algorithm. Check this before you use them. /// /// This is totally unoptimized, so for large polygons it should not be part /// of the simulation loop. /// </summary> /// <remarks> /// Only works on simple polygons. /// </remarks> private static List<Vertices> TriangulatePolygon(Vertices vertices, float tolerance) { List<Vertices> results = new List<Vertices>(); //Recurse and split on pinch points Vertices pA, pB; Vertices pin = new Vertices(vertices); if (ResolvePinchPoint(pin, out pA, out pB, tolerance)) { List<Vertices> mergeA = TriangulatePolygon(pA, tolerance); List<Vertices> mergeB = TriangulatePolygon(pB, tolerance); if (mergeA.Count == -1 || mergeB.Count == -1) throw new Exception("Can't triangulate your polygon."); for (int i = 0; i < mergeA.Count; ++i) { results.Add(new Vertices(mergeA[i])); } for (int i = 0; i < mergeB.Count; ++i) { results.Add(new Vertices(mergeB[i])); } return results; } Vertices[] buffer = new Vertices[vertices.Count - 2]; int bufferSize = 0; float[] xrem = new float[vertices.Count]; float[] yrem = new float[vertices.Count]; for (int i = 0; i < vertices.Count; ++i) { xrem[i] = vertices[i].X; yrem[i] = vertices[i].Y; } int vNum = vertices.Count; while (vNum > 3) { // Find an ear int earIndex = -1; float earMaxMinCross = -10.0f; for (int i = 0; i < vNum; ++i) { if (IsEar(i, xrem, yrem, vNum)) { int lower = Remainder(i - 1, vNum); int upper = Remainder(i + 1, vNum); Vector2 d1 = new Vector2(xrem[upper] - xrem[i], yrem[upper] - yrem[i]); Vector2 d2 = new Vector2(xrem[i] - xrem[lower], yrem[i] - yrem[lower]); Vector2 d3 = new Vector2(xrem[lower] - xrem[upper], yrem[lower] - yrem[upper]); d1.Normalize(); d2.Normalize(); d3.Normalize(); float cross12; MathUtils.Cross(ref d1, ref d2, out cross12); cross12 = Math.Abs(cross12); float cross23; MathUtils.Cross(ref d2, ref d3, out cross23); cross23 = Math.Abs(cross23); float cross31; MathUtils.Cross(ref d3, ref d1, out cross31); cross31 = Math.Abs(cross31); //Find the maximum minimum angle float minCross = Math.Min(cross12, Math.Min(cross23, cross31)); if (minCross > earMaxMinCross) { earIndex = i; earMaxMinCross = minCross; } } } // If we still haven't found an ear, we're screwed. // Note: sometimes this is happening because the // remaining points are collinear. Really these // should just be thrown out without halting triangulation. if (earIndex == -1) { for (int i = 0; i < bufferSize; i++) { results.Add(buffer[i]); } return results; } // Clip off the ear: // - remove the ear tip from the list --vNum; float[] newx = new float[vNum]; float[] newy = new float[vNum]; int currDest = 0; for (int i = 0; i < vNum; ++i) { if (currDest == earIndex) ++currDest; newx[i] = xrem[currDest]; newy[i] = yrem[currDest]; ++currDest; } // - add the clipped triangle to the triangle list int under = (earIndex == 0) ? (vNum) : (earIndex - 1); int over = (earIndex == vNum) ? 0 : (earIndex + 1); Triangle toAdd = new Triangle(xrem[earIndex], yrem[earIndex], xrem[over], yrem[over], xrem[under], yrem[under]); buffer[bufferSize] = toAdd; ++bufferSize; // - replace the old list with the new one xrem = newx; yrem = newy; } Triangle tooAdd = new Triangle(xrem[1], yrem[1], xrem[2], yrem[2], xrem[0], yrem[0]); buffer[bufferSize] = tooAdd; ++bufferSize; for (int i = 0; i < bufferSize; i++) { results.Add(new Vertices(buffer[i])); } return results; }
public Triangle(Triangle t) { X = new float[3]; Y = new float[3]; X[0] = t.X[0]; X[1] = t.X[1]; X[2] = t.X[2]; Y[0] = t.Y[0]; Y[1] = t.Y[1]; Y[2] = t.Y[2]; }
private static Vertices AddTriangle(Triangle t, Vertices vertices) { // First, find vertices that connect int firstP = -1; int firstT = -1; int secondP = -1; int secondT = -1; for (int i = 0; i < vertices.Count; i++) { if (t.X[0] == vertices[i].X && t.Y[0] == vertices[i].Y) { if (firstP == -1) { firstP = i; firstT = 0; } else { secondP = i; secondT = 0; } } else if (t.X[1] == vertices[i].X && t.Y[1] == vertices[i].Y) { if (firstP == -1) { firstP = i; firstT = 1; } else { secondP = i; secondT = 1; } } else if (t.X[2] == vertices[i].X && t.Y[2] == vertices[i].Y) { if (firstP == -1) { firstP = i; firstT = 2; } else { secondP = i; secondT = 2; } } } // Fix ordering if first should be last vertex of poly if (firstP == 0 && secondP == vertices.Count - 1) { firstP = vertices.Count - 1; secondP = 0; } // Didn't find it if (secondP == -1) { return null; } // Find tip index on triangle int tipT = 0; if (tipT == firstT || tipT == secondT) tipT = 1; if (tipT == firstT || tipT == secondT) tipT = 2; Vertices result = new Vertices(vertices.Count + 1); for (int i = 0; i < vertices.Count; i++) { result.Add(vertices[i]); if (i == firstP) result.Add(new Vector2(t.X[tipT], t.Y[tipT])); } return result; }
/// <summary> /// Triangulates a polygon using simple ear-clipping algorithm. Returns /// size of Triangle array unless the polygon can't be triangulated. /// This should only happen if the polygon self-intersects, /// though it will not _always_ return null for a bad polygon - it is the /// caller's responsibility to check for self-intersection, and if it /// doesn't, it should at least check that the return value is non-null /// before using. You're warned! /// /// Triangles may be degenerate, especially if you have identical points /// in the input to the algorithm. Check this before you use them. /// /// This is totally unoptimized, so for large polygons it should not be part /// of the simulation loop. /// </summary> /// <remarks> /// Only works on simple polygons. /// </remarks> static List <Vertices> TriangulatePolygon(Vertices vertices, float tolerance) { //FPE note: Check is needed as invalid triangles can be returned in recursive calls. if (vertices.Count < 3) { return(new List <Vertices>()); } var results = new List <Vertices>(); //Recurse and split on pinch points Vertices pA, pB; var pin = new Vertices(vertices); if (ResolvePinchPoint(pin, out pA, out pB, tolerance)) { var mergeA = TriangulatePolygon(pA, tolerance); var mergeB = TriangulatePolygon(pB, tolerance); if (mergeA.Count == -1 || mergeB.Count == -1) { throw new Exception("Can't triangulate your polygon."); } for (int i = 0; i < mergeA.Count; ++i) { results.Add(new Vertices(mergeA[i])); } for (int i = 0; i < mergeB.Count; ++i) { results.Add(new Vertices(mergeB[i])); } return(results); } var buffer = new Vertices[vertices.Count - 2]; var bufferSize = 0; var xrem = new float[vertices.Count]; var yrem = new float[vertices.Count]; for (int i = 0; i < vertices.Count; ++i) { xrem[i] = vertices[i].X; yrem[i] = vertices[i].Y; } var vNum = vertices.Count; while (vNum > 3) { // Find an ear var earIndex = -1; var earMaxMinCross = -10.0f; for (int i = 0; i < vNum; ++i) { if (IsEar(i, xrem, yrem, vNum)) { var lower = Remainder(i - 1, vNum); var upper = Remainder(i + 1, vNum); var d1 = new Vector2(xrem[upper] - xrem[i], yrem[upper] - yrem[i]); var d2 = new Vector2(xrem[i] - xrem[lower], yrem[i] - yrem[lower]); var d3 = new Vector2(xrem[lower] - xrem[upper], yrem[lower] - yrem[upper]); Nez.Vector2Ext.Normalize(ref d1); Nez.Vector2Ext.Normalize(ref d2); Nez.Vector2Ext.Normalize(ref d3); float cross12; MathUtils.Cross(ref d1, ref d2, out cross12); cross12 = Math.Abs(cross12); float cross23; MathUtils.Cross(ref d2, ref d3, out cross23); cross23 = Math.Abs(cross23); float cross31; MathUtils.Cross(ref d3, ref d1, out cross31); cross31 = Math.Abs(cross31); //Find the maximum minimum angle float minCross = Math.Min(cross12, Math.Min(cross23, cross31)); if (minCross > earMaxMinCross) { earIndex = i; earMaxMinCross = minCross; } } } // If we still haven't found an ear, we're screwed. // Note: sometimes this is happening because the // remaining points are collinear. Really these // should just be thrown out without halting triangulation. if (earIndex == -1) { for (int i = 0; i < bufferSize; i++) { results.Add(buffer[i]); } return(results); } // Clip off the ear: // - remove the ear tip from the list --vNum; float[] newx = new float[vNum]; float[] newy = new float[vNum]; int currDest = 0; for (int i = 0; i < vNum; ++i) { if (currDest == earIndex) { ++currDest; } newx[i] = xrem[currDest]; newy[i] = yrem[currDest]; ++currDest; } // - add the clipped triangle to the triangle list int under = (earIndex == 0) ? (vNum) : (earIndex - 1); int over = (earIndex == vNum) ? 0 : (earIndex + 1); var toAdd = new Triangle(xrem[earIndex], yrem[earIndex], xrem[over], yrem[over], xrem[under], yrem[under]); buffer[bufferSize] = toAdd; ++bufferSize; // - replace the old list with the new one xrem = newx; yrem = newy; } var tooAdd = new Triangle(xrem[1], yrem[1], xrem[2], yrem[2], xrem[0], yrem[0]); buffer[bufferSize] = tooAdd; ++bufferSize; for (int i = 0; i < bufferSize; i++) { results.Add(new Vertices(buffer[i])); } return(results); }
public Triangle(Triangle t) { x = new float[3]; y = new float[3]; x[0] = t.x[0]; x[1] = t.x[1]; x[2] = t.x[2]; y[0] = t.y[0]; y[1] = t.y[1]; y[2] = t.y[2]; }