123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691 |
- /*
- * C# Version Ported by Matt Bettcher and Ian Qvist 2009-2010
- *
- * Original C++ Version Copyright (c) 2007 Eric Jordan
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- */
- using System;
- using System.Collections.Generic;
- using FarseerPhysics.Common.PolygonManipulation;
- using Microsoft.Xna.Framework;
- namespace FarseerPhysics.Common.Decomposition
- {
- /// <summary>
- /// Ported from jBox2D. Original author: ewjordan
- /// Triangulates a polygon using simple ear-clipping algorithm.
- ///
- /// Only works on simple polygons.
- ///
- /// Triangles may be degenerate, especially if you have identical points
- /// in the input to the algorithm. Check this before you use them.
- /// </summary>
- public static class EarclipDecomposer
- {
- //box2D rev 32 - for details, see http://www.box2d.org/forum/viewtopic.php?f=4&t=83&start=50
- private const float Tol = .001f;
- /// <summary>
- /// Decomposes a non-convex polygon into a number of convex polygons, up
- /// to maxPolys (remaining pieces are thrown out).
- ///
- /// Each resulting polygon will have no more than Settings.MaxPolygonVertices
- /// vertices.
- ///
- /// Warning: Only works on simple polygons
- /// </summary>
- /// <param name="vertices">The vertices.</param>
- /// <returns></returns>
- public static List<Vertices> ConvexPartition(Vertices vertices)
- {
- return ConvexPartition(vertices, int.MaxValue, 0);
- }
- /// <summary>
- /// Decomposes a non-convex polygon into a number of convex polygons, up
- /// to maxPolys (remaining pieces are thrown out).
- /// Each resulting polygon will have no more than Settings.MaxPolygonVertices
- /// vertices.
- /// Warning: Only works on simple polygons
- /// </summary>
- /// <param name="vertices">The vertices.</param>
- /// <param name="maxPolys">The maximum number of polygons.</param>
- /// <param name="tolerance">The tolerance.</param>
- /// <returns></returns>
- public static List<Vertices> ConvexPartition(Vertices vertices, int maxPolys, float tolerance)
- {
- if (vertices.Count < 3)
- return new List<Vertices> { vertices };
- /*
- if (vertices.IsConvex() && vertices.Count <= Settings.MaxPolygonVertices)
- {
- if (vertices.IsCounterClockWise())
- {
- Vertices tempP = new Vertices(vertices);
- tempP.Reverse();
- tempP = SimplifyTools.CollinearSimplify(tempP);
- tempP.ForceCounterClockWise();
- return new List<Vertices> { tempP };
- }
- vertices = SimplifyTools.CollinearSimplify(vertices);
- vertices.ForceCounterClockWise();
- return new List<Vertices> { vertices };
- }
- */
- List<Triangle> triangulated;
- if (vertices.IsCounterClockWise())
- {
- Vertices tempP = new Vertices(vertices);
- tempP.Reverse();
- triangulated = TriangulatePolygon(tempP);
- }
- else
- {
- triangulated = TriangulatePolygon(vertices);
- }
- if (triangulated.Count < 1)
- {
- //Still no luck? Oh well...
- throw new Exception("Can't triangulate your polygon.");
- }
- List<Vertices> polygonizedTriangles = PolygonizeTriangles(triangulated, maxPolys, tolerance);
- //The polygonized triangles are not guaranteed to be without collinear points. We remove
- //them to be sure.
- for (int i = 0; i < polygonizedTriangles.Count; i++)
- {
- polygonizedTriangles[i] = SimplifyTools.CollinearSimplify(polygonizedTriangles[i], 0);
- }
- //Remove empty vertice collections
- for (int i = polygonizedTriangles.Count - 1; i >= 0; i--)
- {
- if (polygonizedTriangles[i].Count == 0)
- polygonizedTriangles.RemoveAt(i);
- }
- return polygonizedTriangles;
- }
- /// <summary>
- /// Turns a list of triangles into a list of convex polygons. Very simple
- /// method - start with a seed triangle, keep adding triangles to it until
- /// you can't add any more without making the polygon non-convex.
- ///
- /// Returns an integer telling how many polygons were created. Will fill
- /// polys array up to polysLength entries, which may be smaller or larger
- /// than the return value.
- ///
- /// Takes O(N///P) where P is the number of resultant polygons, N is triangle
- /// count.
- ///
- /// The final polygon list will not necessarily be minimal, though in
- /// practice it works fairly well.
- /// </summary>
- /// <param name="triangulated">The triangulated.</param>
- ///<param name="maxPolys">The maximun number of polygons</param>
- ///<param name="tolerance">The tolerance</param>
- ///<returns></returns>
- public static List<Vertices> PolygonizeTriangles(List<Triangle> triangulated, int maxPolys, float tolerance)
- {
- List<Vertices> polys = new List<Vertices>(50);
- int polyIndex = 0;
- if (triangulated.Count <= 0)
- {
- //return empty polygon list
- return polys;
- }
- bool[] covered = new bool[triangulated.Count];
- for (int i = 0; i < triangulated.Count; ++i)
- {
- covered[i] = false;
- //Check here for degenerate triangles
- if (((triangulated[i].X[0] == triangulated[i].X[1]) && (triangulated[i].Y[0] == triangulated[i].Y[1]))
- ||
- ((triangulated[i].X[1] == triangulated[i].X[2]) && (triangulated[i].Y[1] == triangulated[i].Y[2]))
- ||
- ((triangulated[i].X[0] == triangulated[i].X[2]) && (triangulated[i].Y[0] == triangulated[i].Y[2])))
- {
- covered[i] = true;
- }
- }
- bool notDone = true;
- while (notDone)
- {
- int currTri = -1;
- for (int i = 0; i < triangulated.Count; ++i)
- {
- if (covered[i])
- continue;
- currTri = i;
- break;
- }
- if (currTri == -1)
- {
- notDone = false;
- }
- else
- {
- Vertices poly = new Vertices(3);
- for (int i = 0; i < 3; i++)
- {
- poly.Add(new Vector2(triangulated[currTri].X[i], triangulated[currTri].Y[i]));
- }
- covered[currTri] = true;
- int index = 0;
- for (int i = 0; i < 2 * triangulated.Count; ++i, ++index)
- {
- while (index >= triangulated.Count) index -= triangulated.Count;
- if (covered[index])
- {
- continue;
- }
- Vertices newP = AddTriangle(triangulated[index], poly);
- if (newP == null)
- continue; // is this right
- if (newP.Count > Settings.MaxPolygonVertices)
- continue;
- if (newP.IsConvex())
- {
- //Or should it be IsUsable? Maybe re-write IsConvex to apply the angle threshold from Box2d
- poly = new Vertices(newP);
- covered[index] = true;
- }
- }
- //We have a maximum of polygons that we need to keep under.
- if (polyIndex < maxPolys)
- {
- //SimplifyTools.MergeParallelEdges(poly, tolerance);
- //If identical points are present, a triangle gets
- //borked by the MergeParallelEdges function, hence
- //the vertex number check
- if (poly.Count >= 3)
- polys.Add(new Vertices(poly));
- //else
- // printf("Skipping corrupt poly\n");
- }
- if (poly.Count >= 3)
- polyIndex++; //Must be outside (polyIndex < polysLength) test
- }
- }
- return polys;
- }
- /// <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);
- 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(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;
- }
- /// <summary>
- /// Finds and fixes "pinch points," points where two polygon
- /// vertices are at the same point.
- ///
- /// If a pinch point is found, pin is broken up into poutA and poutB
- /// and true is returned; otherwise, returns false.
- ///
- /// Mostly for internal use.
- ///
- /// O(N^2) time, which sucks...
- /// </summary>
- /// <param name="pin">The pin.</param>
- /// <param name="poutA">The pout A.</param>
- /// <param name="poutB">The pout B.</param>
- /// <returns></returns>
- private static bool ResolvePinchPoint(Vertices pin, out Vertices poutA, out Vertices poutB)
- {
- poutA = new Vertices();
- poutB = new Vertices();
- if (pin.Count < 3)
- return false;
- bool hasPinchPoint = false;
- int pinchIndexA = -1;
- int pinchIndexB = -1;
- for (int i = 0; i < pin.Count; ++i)
- {
- for (int j = i + 1; j < pin.Count; ++j)
- {
- //Don't worry about pinch points where the points
- //are actually just dupe neighbors
- if (Math.Abs(pin[i].X - pin[j].X) < Tol && Math.Abs(pin[i].Y - pin[j].Y) < Tol && j != i + 1)
- {
- pinchIndexA = i;
- pinchIndexB = j;
- hasPinchPoint = true;
- break;
- }
- }
- if (hasPinchPoint) break;
- }
- if (hasPinchPoint)
- {
- int sizeA = pinchIndexB - pinchIndexA;
- if (sizeA == pin.Count) return false; //has dupe points at wraparound, not a problem here
- for (int i = 0; i < sizeA; ++i)
- {
- int ind = Remainder(pinchIndexA + i, pin.Count); // is this right
- poutA.Add(pin[ind]);
- }
- int sizeB = pin.Count - sizeA;
- for (int i = 0; i < sizeB; ++i)
- {
- int ind = Remainder(pinchIndexB + i, pin.Count); // is this right
- poutB.Add(pin[ind]);
- }
- }
- return hasPinchPoint;
- }
- /// <summary>
- /// Fix for obnoxious behavior for the % operator for negative numbers...
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="modulus">The modulus.</param>
- /// <returns></returns>
- private static int Remainder(int x, int modulus)
- {
- int rem = x % modulus;
- while (rem < 0)
- {
- rem += modulus;
- }
- return rem;
- }
- 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>
- /// Checks if vertex i is the tip of an ear in polygon defined by xv[] and
- /// yv[].
- ///
- /// Assumes clockwise orientation of polygon...ick
- /// </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>
- /// <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;
- }
- }
- public class Triangle
- {
- public float[] X;
- public float[] Y;
- //Constructor automatically fixes orientation to ccw
- public Triangle(float x1, float y1, float x2, float y2, float x3, float y3)
- {
- X = new float[3];
- Y = new float[3];
- float dx1 = x2 - x1;
- float dx2 = x3 - x1;
- float dy1 = y2 - y1;
- float dy2 = y3 - y1;
- float cross = dx1 * dy2 - dx2 * dy1;
- bool ccw = (cross > 0);
- if (ccw)
- {
- X[0] = x1;
- X[1] = x2;
- X[2] = x3;
- Y[0] = y1;
- Y[1] = y2;
- Y[2] = y3;
- }
- else
- {
- X[0] = x1;
- X[1] = x3;
- X[2] = x2;
- Y[0] = y1;
- Y[1] = y3;
- Y[2] = y2;
- }
- }
- 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];
- }
- public bool IsInside(float x, float y)
- {
- if (x < X[0] && x < X[1] && x < X[2]) return false;
- if (x > X[0] && x > X[1] && x > X[2]) return false;
- if (y < Y[0] && y < Y[1] && y < Y[2]) return false;
- if (y > Y[0] && y > Y[1] && y > Y[2]) return false;
- float vx2 = x - X[0];
- float vy2 = y - Y[0];
- float vx1 = X[1] - X[0];
- float vy1 = Y[1] - Y[0];
- float vx0 = X[2] - X[0];
- float vy0 = Y[2] - Y[0];
- float dot00 = vx0 * vx0 + vy0 * vy0;
- float dot01 = vx0 * vx1 + vy0 * vy1;
- float dot02 = vx0 * vx2 + vy0 * vy2;
- float dot11 = vx1 * vx1 + vy1 * vy1;
- float dot12 = vx1 * vx2 + vy1 * vy2;
- float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
- float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
- float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
- return ((u > 0) && (v > 0) && (u + v < 1));
- }
- }
- }
|