public PolyNode GetRightestConnection(PolyNode incoming) { if (NConnected == 0) { Debug.Assert(false); // This means the connection graph is inconsistent } if (NConnected == 1) { //b2Assert(false); // Because of the possibility of collapsing nearby points, // we may end up with "spider legs" dangling off of a region. // The correct behavior here is to turn around. return(incoming); } FVector2 inDir = Position - incoming.Position; float inLength = inDir.Length(); inDir.Normalize(); Debug.Assert(inLength > Box2D.Settings.b2_epsilon); PolyNode result = null; for (int i = 0; i < NConnected; ++i) { if (Connected[i] == incoming) { continue; } FVector2 testDir = Connected[i].Position - Position; float testLengthSqr = testDir.LengthSquared(); testDir.Normalize(); Debug.Assert(testLengthSqr >= Box2D.Settings.b2_epsilon * Box2D.Settings.b2_epsilon); float myCos = FVector2.Dot(inDir, testDir); float mySin = MathUtils.Cross(inDir, testDir); if (result != null) { FVector2 resultDir = result.Position - Position; resultDir.Normalize(); float resCos = FVector2.Dot(inDir, resultDir); float resSin = MathUtils.Cross(inDir, resultDir); if (IsRighter(mySin, myCos, resSin, resCos)) { result = Connected[i]; } } else { result = Connected[i]; } } Debug.Assert(result != null); 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. /// /// Warning: Only works on simple polygons. /// </summary> /// <returns></returns> public static List <Triangle> TriangulatePolygon(Vertices vertices) { List <Triangle> results = new List <Triangle>(); if (vertices.Count < 3) { return(new List <Triangle>()); } //Recurse and split on pinch points Vertices pA, pB; Vertices pin = new Vertices(vertices); if (ResolvePinchPoint(pin, out pA, out pB)) { List <Triangle> mergeA = TriangulatePolygon(pA); List <Triangle> mergeB = TriangulatePolygon(pB); 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 Triangle(mergeA[i])); } for (int i = 0; i < mergeB.Count; ++i) { results.Add(new Triangle(mergeB[i])); } return(results); } Triangle[] buffer = new Triangle[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); FVector2 d1 = new FVector2(xrem[upper] - xrem[i], yrem[upper] - yrem[i]); FVector2 d2 = new FVector2(xrem[i] - xrem[lower], yrem[i] - yrem[lower]); FVector2 d3 = new FVector2(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(new Triangle(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 Triangle(buffer[i])); } return(results); }