123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587 |
- /*
- * 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.
- */
- // This utility works with Box2d version 2.0 (or higher), and not with 1.4.3
- #include "b2Triangle.h"
- #include "b2Polygon.h"
- #include <math.h>
- #include <limits.h>
- #include <assert.h>
- #define b2Assert assert
- namespace b2ConvexDecomp {
- //If you're using 1.4.3, b2_toiSlop won't exist, so set this equal to 0
- static const float32 toiSlop = 0.0f;
- /*
- * Check if the lines a0->a1 and b0->b1 cross.
- * If they do, intersectionPoint will be filled
- * with the point of crossing.
- *
- * Grazing lines should not return true.
- */
- bool intersect(const b2Vec2& a0, const b2Vec2& a1,
- const b2Vec2& b0, const b2Vec2& b1,
- b2Vec2& intersectionPoint) {
- if (a0 == b0 || a0 == b1 || a1 == b0 || a1 == b1) return false;
- float x1 = a0.x; float y1 = a0.y;
- float x2 = a1.x; float y2 = a1.y;
- float x3 = b0.x; float y3 = b0.y;
- float x4 = b1.x; float y4 = b1.y;
-
- //AABB early exit
- if (b2Max(x1,x2) < b2Min(x3,x4) || b2Max(x3,x4) < b2Min(x1,x2) ) return false;
- if (b2Max(y1,y2) < b2Min(y3,y4) || b2Max(y3,y4) < b2Min(y1,y2) ) return false;
-
- float ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3));
- float ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3));
- float denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
- if (b2Abs(denom) < CMP_EPSILON) {
- //Lines are too close to parallel to call
- return false;
- }
- ua /= denom;
- ub /= denom;
-
- if ((0 < ua) && (ua < 1) && (0 < ub) && (ub < 1)) {
- //if (intersectionPoint){
- intersectionPoint.x = (x1 + ua * (x2 - x1));
- intersectionPoint.y = (y1 + ua * (y2 - y1));
- //}
- //printf("%f, %f -> %f, %f crosses %f, %f -> %f, %f\n",x1,y1,x2,y2,x3,y3,x4,y4);
- return true;
- }
-
- return false;
- }
- /*
- * True if line from a0->a1 intersects b0->b1
- */
- bool intersect(const b2Vec2& a0, const b2Vec2& a1,
- const b2Vec2& b0, const b2Vec2& b1) {
- b2Vec2 myVec(0.0f,0.0f);
- return intersect(a0, a1, b0, b1, myVec);
- }
- b2Polygon::b2Polygon(float32* _x, float32* _y, int32 nVert) {
- nVertices = nVert;
- x = new float32[nVertices];
- y = new float32[nVertices];
- for (int32 i = 0; i < nVertices; ++i) {
- x[i] = _x[i];
- y[i] = _y[i];
- }
- areaIsSet = false;
- }
-
- b2Polygon::b2Polygon(b2Vec2* v, int32 nVert) {
- nVertices = nVert;
- x = new float32[nVertices];
- y = new float32[nVertices];
- for (int32 i = 0; i < nVertices; ++i) {
- x[i] = v[i].x;
- y[i] = v[i].y;
- }
- areaIsSet = false;
- }
- b2Polygon::b2Polygon() {
- x = NULL;
- y = NULL;
- nVertices = 0;
- areaIsSet = false;
- }
-
- b2Polygon::~b2Polygon() {
- //printf("About to delete poly with %d vertices\n",nVertices);
- delete[] x;
- delete[] y;
- }
- float32 b2Polygon::GetArea() {
- // TODO: fix up the areaIsSet caching so that it can be used
- //if (areaIsSet) return area;
- area = 0.0f;
-
- //First do wraparound
- area += x[nVertices-1]*y[0]-x[0]*y[nVertices-1];
- for (int i=0; i<nVertices-1; ++i){
- area += x[i]*y[i+1]-x[i+1]*y[i];
- }
- area *= .5f;
- areaIsSet = true;
- return area;
- }
- bool b2Polygon::IsCCW() {
- return (GetArea() > 0.0f);
- }
-
- void b2Polygon::MergeParallelEdges(float32 tolerance) {
- if (nVertices <= 3) return; //Can't do anything useful here to a triangle
- bool* mergeMe = new bool[nVertices];
- int32 newNVertices = nVertices;
- for (int32 i = 0; i < nVertices; ++i) {
- int32 lower = (i == 0) ? (nVertices - 1) : (i - 1);
- int32 middle = i;
- int32 upper = (i == nVertices - 1) ? (0) : (i + 1);
- float32 dx0 = x[middle] - x[lower];
- float32 dy0 = y[middle] - y[lower];
- float32 dx1 = x[upper] - x[middle];
- float32 dy1 = y[upper] - y[middle];
- float32 norm0 = sqrtf(dx0*dx0+dy0*dy0);
- float32 norm1 = sqrtf(dx1*dx1+dy1*dy1);
- if ( !(norm0 > 0.0f && norm1 > 0.0f) && newNVertices > 3 ) {
- //Merge identical points
- mergeMe[i] = true;
- --newNVertices;
- }
- dx0 /= norm0; dy0 /= norm0;
- dx1 /= norm1; dy1 /= norm1;
- float32 cross = dx0 * dy1 - dx1 * dy0;
- float32 dot = dx0 * dx1 + dy0 * dy1;
- if (fabs(cross) < tolerance && dot > 0 && newNVertices > 3) {
- mergeMe[i] = true;
- --newNVertices;
- } else {
- mergeMe[i] = false;
- }
- }
- if(newNVertices == nVertices || newNVertices == 0) {
- delete[] mergeMe;
- return;
- }
- float32* newx = new float32[newNVertices];
- float32* newy = new float32[newNVertices];
- int32 currIndex = 0;
- for (int32 i=0; i < nVertices; ++i) {
- if (mergeMe[i] || newNVertices == 0 || currIndex == newNVertices) continue;
- b2Assert(currIndex < newNVertices);
- newx[currIndex] = x[i];
- newy[currIndex] = y[i];
- ++currIndex;
- }
- delete[] x;
- delete[] y;
- delete[] mergeMe;
- x = newx;
- y = newy;
- nVertices = newNVertices;
- //printf("%d \n", newNVertices);
- }
-
- /*
- * Allocates and returns pointer to vector vertex array.
- * Length of array is nVertices.
- */
- b2Vec2* b2Polygon::GetVertexVecs() {
- b2Vec2* out = new b2Vec2[nVertices];
- for (int32 i = 0; i < nVertices; ++i) {
- out[i].Set(x[i], y[i]);
- }
- return out;
- }
-
- b2Polygon::b2Polygon(b2Triangle& t) {
- nVertices = 3;
- x = new float[nVertices];
- y = new float[nVertices];
- for (int32 i = 0; i < nVertices; ++i) {
- x[i] = t.x[i];
- y[i] = t.y[i];
- }
- }
-
- void b2Polygon::Set(const b2Polygon& p) {
- if (nVertices != p.nVertices){
- nVertices = p.nVertices;
- delete[] x;
- delete[] y;
- x = new float32[nVertices];
- y = new float32[nVertices];
- }
-
- for (int32 i = 0; i < nVertices; ++i) {
- x[i] = p.x[i];
- y[i] = p.y[i];
- }
- areaIsSet = false;
- }
-
- /*
- * Assuming the polygon is simple, checks if it is convex.
- */
- bool b2Polygon::IsConvex() {
- bool isPositive = false;
- for (int32 i = 0; i < nVertices; ++i) {
- int32 lower = (i == 0) ? (nVertices - 1) : (i - 1);
- int32 middle = i;
- int32 upper = (i == nVertices - 1) ? (0) : (i + 1);
- float32 dx0 = x[middle] - x[lower];
- float32 dy0 = y[middle] - y[lower];
- float32 dx1 = x[upper] - x[middle];
- float32 dy1 = y[upper] - y[middle];
- float32 cross = dx0 * dy1 - dx1 * dy0;
- // Cross product should have same sign
- // for each vertex if poly is convex.
- bool newIsP = (cross >= 0) ? true : false;
- if (i == 0) {
- isPositive = newIsP;
- }
- else if (isPositive != newIsP) {
- return false;
- }
- }
- return true;
- }
- /*
- * Pulled from b2Shape.cpp, assertions removed
- */
- static b2Vec2 PolyCentroid(const b2Vec2* vs, int32 count)
- {
- b2Vec2 c; c.Set(0.0f, 0.0f);
- float32 area = 0.0f;
- const float32 inv3 = 1.0f / 3.0f;
- b2Vec2 pRef(0.0f, 0.0f);
- for (int32 i = 0; i < count; ++i)
- {
- // Triangle vertices.
- b2Vec2 p1 = pRef;
- b2Vec2 p2 = vs[i];
- b2Vec2 p3 = i + 1 < count ? vs[i+1] : vs[0];
- b2Vec2 e1 = p2 - p1;
- b2Vec2 e2 = p3 - p1;
- float32 D = b2Cross(e1, e2);
- float32 triangleArea = 0.5f * D;
- area += triangleArea;
- // Area weighted centroid
- c += (p1 + p2 + p3) * triangleArea * inv3;
- }
- // Centroid
- c *= 1.0f / area;
- return c;
- }
- /*
- * Checks if polygon is valid for use in Box2d engine.
- * Last ditch effort to ensure no invalid polygons are
- * added to world geometry.
- *
- * Performs a full check, for simplicity, convexity,
- * orientation, minimum angle, and volume. This won't
- * be very efficient, and a lot of it is redundant when
- * other tools in this section are used.
- */
- bool b2Polygon::IsUsable(bool printErrors){
- int32 error = -1;
- bool noError = true;
- if (nVertices < 3 || nVertices > b2_maxPolygonVertices) {noError = false; error = 0;}
- if (!IsConvex()) {noError = false; error = 1;}
- if (!IsSimple()) {noError = false; error = 2;}
- if (GetArea() < CMP_EPSILON) {noError = false; error = 3;}
- //Compute normals
- b2Vec2* normals = new b2Vec2[nVertices];
- b2Vec2* vertices = new b2Vec2[nVertices];
- for (int32 i = 0; i < nVertices; ++i){
- vertices[i].Set(x[i],y[i]);
- int32 i1 = i;
- int32 i2 = i + 1 < nVertices ? i + 1 : 0;
- b2Vec2 edge(x[i2]-x[i1],y[i2]-y[i1]);
- normals[i] = b2Cross(edge, 1.0f);
- normals[i].Normalize();
- }
- //Required side checks
- for (int32 i=0; i<nVertices; ++i){
- int32 iminus = (i==0)?nVertices-1:i-1;
- //int32 iplus = (i==nVertices-1)?0:i+1;
- //Parallel sides check
- float32 cross = b2Cross(normals[iminus], normals[i]);
- cross = b2Clamp(cross, -1.0f, 1.0f);
- float32 angle = asinf(cross);
- if(angle <= b2_angularSlop){
- noError = false;
- error = 4;
- break;
- }
- //Too skinny check
- for (int32 j=0; j<nVertices; ++j){
- if (j == i || j == (i + 1) % nVertices){
- continue;
- }
- float32 s = b2Dot(normals[i], vertices[j] - vertices[i]);
- if (s >= -b2_linearSlop){
- noError = false;
- error = 5;
- }
- }
- b2Vec2 centroid = PolyCentroid(vertices,nVertices);
- b2Vec2 n1 = normals[iminus];
- b2Vec2 n2 = normals[i];
- b2Vec2 v = vertices[i] - centroid;
- b2Vec2 d;
- d.x = b2Dot(n1, v) - toiSlop;
- d.y = b2Dot(n2, v) - toiSlop;
- // Shifting the edge inward by b2_toiSlop should
- // not cause the plane to pass the centroid.
- if ((d.x < 0.0f)||(d.y < 0.0f)){
- noError = false;
- error = 6;
- }
- }
- delete[] vertices;
- delete[] normals;
- if (!noError && printErrors){
- printf("Found invalid polygon, ");
- switch(error){
- case 0:
- printf("must have between 3 and %d vertices.\n",b2_maxPolygonVertices);
- break;
- case 1:
- printf("must be convex.\n");
- break;
- case 2:
- printf("must be simple (cannot intersect itself).\n");
- break;
- case 3:
- printf("area is too small.\n");
- break;
- case 4:
- printf("sides are too close to parallel.\n");
- break;
- case 5:
- printf("polygon is too thin.\n");
- break;
- case 6:
- printf("core shape generation would move edge past centroid (too thin).\n");
- break;
- default:
- printf("don't know why.\n");
- }
- }
- return noError;
- }
- bool b2Polygon::IsUsable(){
- return IsUsable(B2_POLYGON_REPORT_ERRORS);
- }
- //Check for edge crossings
- bool b2Polygon::IsSimple() {
- for (int32 i=0; i<nVertices; ++i){
- int32 iplus = (i+1 > nVertices-1)?0:i+1;
- b2Vec2 a1(x[i],y[i]);
- b2Vec2 a2(x[iplus],y[iplus]);
- for (int32 j=i+1; j<nVertices; ++j){
- int32 jplus = (j+1 > nVertices-1)?0:j+1;
- b2Vec2 b1(x[j],y[j]);
- b2Vec2 b2(x[jplus],y[jplus]);
- if (intersect(a1,a2,b1,b2)){
- return false;
- }
- }
- }
- return true;
- }
-
- /*
- * Tries to add a triangle to the polygon. Returns null if it can't connect
- * properly, otherwise returns a pointer to the new Polygon. Assumes bitwise
- * equality of joined vertex positions.
- *
- * Remember to delete the pointer afterwards.
- * Todo: Make this return a b2Polygon instead
- * of a pointer to a heap-allocated one.
- *
- * For internal use.
- */
- b2Polygon* b2Polygon::Add(b2Triangle& t) {
- // First, find vertices that connect
- int32 firstP = -1;
- int32 firstT = -1;
- int32 secondP = -1;
- int32 secondT = -1;
- for (int32 i = 0; i < nVertices; i++) {
- if (t.x[0] == x[i] && t.y[0] == y[i]) {
- if (firstP == -1) {
- firstP = i;
- firstT = 0;
- }
- else {
- secondP = i;
- secondT = 0;
- }
- }
- else if (t.x[1] == x[i] && t.y[1] == y[i]) {
- if (firstP == -1) {
- firstP = i;
- firstT = 1;
- }
- else {
- secondP = i;
- secondT = 1;
- }
- }
- else if (t.x[2] == x[i] && t.y[2] == y[i]) {
- if (firstP == -1) {
- firstP = i;
- firstT = 2;
- }
- else {
- secondP = i;
- secondT = 2;
- }
- }
- else {
- }
- }
- // Fix ordering if first should be last vertex of poly
- if (firstP == 0 && secondP == nVertices - 1) {
- firstP = nVertices - 1;
- secondP = 0;
- }
-
- // Didn't find it
- if (secondP == -1) {
- return NULL;
- }
-
- // Find tip index on triangle
- int32 tipT = 0;
- if (tipT == firstT || tipT == secondT)
- tipT = 1;
- if (tipT == firstT || tipT == secondT)
- tipT = 2;
-
- float32* newx = new float[nVertices + 1];
- float32* newy = new float[nVertices + 1];
- int32 currOut = 0;
- for (int32 i = 0; i < nVertices; i++) {
- newx[currOut] = x[i];
- newy[currOut] = y[i];
- if (i == firstP) {
- ++currOut;
- newx[currOut] = t.x[tipT];
- newy[currOut] = t.y[tipT];
- }
- ++currOut;
- }
- b2Polygon* result = new b2Polygon(newx, newy, nVertices+1);
- delete[] newx;
- delete[] newy;
- return result;
- }
-
- /**
- * Adds this polygon to a PolyDef.
- */
- #if 0
- void b2Polygon::AddTo(b2FixtureDef& pd) {
- if (nVertices < 3) return;
-
- b2Assert(nVertices <= b2_maxPolygonVertices);
-
- b2Vec2* vecs = GetVertexVecs();
- b2Vec2* vecsToAdd = new b2Vec2[nVertices];
- int32 offset = 0;
-
- b2PolygonShape *polyShape = new b2PolygonShape;
- int32 ind;
-
- for (int32 i = 0; i < nVertices; ++i) {
-
- //Omit identical neighbors (including wraparound)
- ind = i - offset;
- if (vecs[i].x==vecs[remainder(i+1,nVertices)].x &&
- vecs[i].y==vecs[remainder(i+1,nVertices)].y){
- offset++;
- continue;
- }
-
- vecsToAdd[ind] = vecs[i];
-
- }
-
- polyShape->Set((const b2Vec2*)vecsToAdd, ind+1);
- pd.shape = polyShape;
-
- delete[] vecs;
- delete[] vecsToAdd;
- }
- #endif
- /**
- * 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.
- */
- bool ResolvePinchPoint(const b2Polygon& pin, b2Polygon& poutA, b2Polygon& poutB){
- if (pin.nVertices < 3) return false;
- float32 tol = .001f;
- bool hasPinchPoint = false;
- int32 pinchIndexA = -1;
- int32 pinchIndexB = -1;
- for (int i=0; i<pin.nVertices; ++i){
- for (int j=i+1; j<pin.nVertices; ++j){
- //Don't worry about pinch points where the points
- //are actually just dupe neighbors
- if (b2Abs(pin.x[i]-pin.x[j])<tol&&b2Abs(pin.y[i]-pin.y[j])<tol&&j!=i+1){
- pinchIndexA = i;
- pinchIndexB = j;
- //printf("pinch: %f, %f == %f, %f\n",pin.x[i],pin.y[i],pin.x[j],pin.y[j]);
- //printf("at indexes %d, %d\n",i,j);
- hasPinchPoint = true;
- break;
- }
- }
- if (hasPinchPoint) break;
- }
- if (hasPinchPoint){
- //printf("Found pinch point\n");
- int32 sizeA = pinchIndexB - pinchIndexA;
- if (sizeA == pin.nVertices) return false;//has dupe points at wraparound, not a problem here
- float32* xA = new float32[sizeA];
- float32* yA = new float32[sizeA];
- for (int32 i=0; i < sizeA; ++i){
- int32 ind = remainder(pinchIndexA+i,pin.nVertices);
- xA[i] = pin.x[ind];
- yA[i] = pin.y[ind];
- }
- b2Polygon tempA(xA,yA,sizeA);
- poutA.Set(tempA);
- delete[] xA;
- delete[] yA;
-
- int32 sizeB = pin.nVertices - sizeA;
- float32* xB = new float32[sizeB];
- float32* yB = new float32[sizeB];
- for (int32 i=0; i<sizeB; ++i){
- int32 ind = remainder(pinchIndexB+i,pin.nVertices);
- xB[i] = pin.x[ind];
- yB[i] = pin.y[ind];
- }
- b2Polygon tempB(xB,yB,sizeB);
- poutB.Set(tempB);
- //printf("Size of a: %d, size of b: %d\n",sizeA,sizeB);
- delete[] xB;
- delete[] yB;
- }
- return hasPinchPoint;
- }
- /**
- * 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.
- *
- * Returns:
- * -1 if algorithm fails (self-intersection most likely)
- * 0 if there are not enough vertices to triangulate anything.
- * Number of triangles if triangulation was successful.
- *
- * results will be filled with results - ear clipping always creates vNum - 2
- * or fewer (due to pinch point polygon snipping), so allocate an array of
- * this size.
- */
-
- int32 TriangulatePolygon(float32* xv, float32* yv, int32 vNum, b2Triangle* results) {
- if (vNum < 3)
- return 0;
- //Recurse and split on pinch points
- b2Polygon pA,pB;
- b2Polygon pin(xv,yv,vNum);
- if (ResolvePinchPoint(pin,pA,pB)){
- b2Triangle* mergeA = new b2Triangle[pA.nVertices];
- b2Triangle* mergeB = new b2Triangle[pB.nVertices];
- int32 nA = TriangulatePolygon(pA.x,pA.y,pA.nVertices,mergeA);
- int32 nB = TriangulatePolygon(pB.x,pB.y,pB.nVertices,mergeB);
- if (nA==-1 || nB==-1){
- delete[] mergeA;
- delete[] mergeB;
- return -1;
- }
- for (int32 i=0; i<nA; ++i){
- results[i].Set(mergeA[i]);
- }
- for (int32 i=0; i<nB; ++i){
- results[nA+i].Set(mergeB[i]);
- }
- delete[] mergeA;
- delete[] mergeB;
- return (nA+nB);
- }
- b2Triangle* buffer = new b2Triangle[vNum-2];
- int32 bufferSize = 0;
- float32* xrem = new float32[vNum];
- float32* yrem = new float32[vNum];
- for (int32 i = 0; i < vNum; ++i) {
- xrem[i] = xv[i];
- yrem[i] = yv[i];
- }
-
- int xremLength = vNum;
-
- while (vNum > 3) {
- // Find an ear
- int32 earIndex = -1;
- //float32 earVolume = -1.0f;
- float32 earMaxMinCross = -10.0f;
- for (int32 i = 0; i < vNum; ++i) {
- if (IsEar(i, xrem, yrem, vNum)) {
- int32 lower = remainder(i-1,vNum);
- int32 upper = remainder(i+1,vNum);
- b2Vec2 d1(xrem[upper]-xrem[i],yrem[upper]-yrem[i]);
- b2Vec2 d2(xrem[i]-xrem[lower],yrem[i]-yrem[lower]);
- b2Vec2 d3(xrem[lower]-xrem[upper],yrem[lower]-yrem[upper]);
- d1.Normalize();
- d2.Normalize();
- d3.Normalize();
- float32 cross12 = b2Abs( b2Cross(d1,d2) );
- float32 cross23 = b2Abs( b2Cross(d2,d3) );
- float32 cross31 = b2Abs( b2Cross(d3,d1) );
- //Find the maximum minimum angle
- float32 minCross = b2Min(cross12, b2Min(cross23,cross31));
- if (minCross > earMaxMinCross){
- earIndex = i;
- earMaxMinCross = minCross;
- }
- /*//This bit chooses the ear with greatest volume first
- float32 testVol = b2Abs( d1.x*d2.y-d2.x*d1.y );
- if (testVol > earVolume){
- earIndex = i;
- earVolume = testVol;
- }*/
- }
- }
-
- // 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){
- if (B2_POLYGON_REPORT_ERRORS){
- b2Polygon dump(xrem,yrem,vNum);
- printf("Couldn't find an ear, dumping remaining poly:\n");
- dump.printFormatted();
- printf("Please submit this dump to ewjordan at Box2d forums\n");
- }
- for (int32 i = 0; i < bufferSize; i++) {
- results[i].Set(buffer[i]);
- }
-
- delete[] buffer;
-
- if (bufferSize > 0) return bufferSize;
- else return -1;
- }
-
- // Clip off the ear:
- // - remove the ear tip from the list
- --vNum;
- float32* newx = new float32[vNum];
- float32* newy = new float32[vNum];
- int32 currDest = 0;
- for (int32 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
- int32 under = (earIndex == 0) ? (vNum) : (earIndex - 1);
- int32 over = (earIndex == vNum) ? 0 : (earIndex + 1);
- b2Triangle toAdd = b2Triangle(xrem[earIndex], yrem[earIndex], xrem[over], yrem[over], xrem[under], yrem[under]);
- buffer[bufferSize].Set(toAdd);
- ++bufferSize;
-
- // - replace the old list with the new one
- delete[] xrem;
- delete[] yrem;
- xrem = newx;
- yrem = newy;
- }
-
- b2Triangle toAdd = b2Triangle(xrem[1], yrem[1], xrem[2], yrem[2],
- xrem[0], yrem[0]);
- buffer[bufferSize].Set(toAdd);
- ++bufferSize;
-
- delete[] xrem;
- delete[] yrem;
-
- b2Assert(bufferSize == xremLength-2);
-
- for (int32 i = 0; i < bufferSize; i++) {
- results[i].Set(buffer[i]);
- }
-
- delete[] buffer;
-
- return bufferSize;
- }
- /**
- * 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.
- */
- int32 PolygonizeTriangles(b2Triangle* triangulated, int32 triangulatedLength, b2Polygon* polys, int32 polysLength) {
- int32 polyIndex = 0;
-
- if (triangulatedLength <= 0) {
- return 0;
- }
- else {
- int* covered = new int[triangulatedLength];
- for (int32 i = 0; i < triangulatedLength; ++i) {
- covered[i] = 0;
- //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] = 1;
- }
- }
-
- bool notDone = true;
- while (notDone) {
- int32 currTri = -1;
- for (int32 i = 0; i < triangulatedLength; ++i) {
- if (covered[i])
- continue;
- currTri = i;
- break;
- }
- if (currTri == -1) {
- notDone = false;
- }
- else {
- b2Polygon poly(triangulated[currTri]);
- covered[currTri] = 1;
- int32 index = 0;
- for (int32 i = 0; i < 2*triangulatedLength; ++i,++index) {
- while (index >= triangulatedLength) index -= triangulatedLength;
- if (covered[index]) {
- continue;
- }
- b2Polygon* newP = poly.Add(triangulated[index]);
- if (!newP) {
- continue;
- }
- if (newP->nVertices > b2Polygon::maxVerticesPerPolygon) {
- delete newP;
- newP = NULL;
- continue;
- }
- if (newP->IsConvex()) { //Or should it be IsUsable? Maybe re-write IsConvex to apply the angle threshold from Box2d
- poly.Set(*newP);
- delete newP;
- newP = NULL;
- covered[index] = 1;
- } else {
- delete newP;
- newP = NULL;
- }
- }
- if (polyIndex < polysLength){
- poly.MergeParallelEdges(b2_angularSlop);
- //If identical points are present, a triangle gets
- //borked by the MergeParallelEdges function, hence
- //the vertex number check
- if (poly.nVertices >= 3) polys[polyIndex].Set(poly);
- //else printf("Skipping corrupt poly\n");
- }
- if (poly.nVertices >= 3) polyIndex++; //Must be outside (polyIndex < polysLength) test
- }
- //printf("MEMCHECK: %d\n",_CrtCheckMemory());
- }
- delete[] covered;
- }
- return polyIndex;
- }
-
- /**
- * Checks if vertex i is the tip of an ear in polygon defined by xv[] and
- * yv[].
- *
- * Assumes clockwise orientation of polygon...ick
- */
- bool IsEar(int32 i, float32* xv, float32* yv, int32 xvLength) {
- float32 dx0, dy0, dx1, dy1;
- dx0 = dy0 = dx1 = dy1 = 0;
- if (i >= xvLength || i < 0 || xvLength < 3) {
- return false;
- }
- int32 upper = i + 1;
- int32 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];
- }
- float32 cross = dx0 * dy1 - dx1 * dy0;
- if (cross > 0)
- return false;
- b2Triangle myTri(xv[i], yv[i], xv[upper], yv[upper],
- xv[lower], yv[lower]);
- for (int32 j = 0; j < xvLength; ++j) {
- if (j == i || j == lower || j == upper)
- continue;
- if (myTri.IsInside(xv[j], yv[j]))
- return false;
- }
- return true;
- }
- void ReversePolygon(b2Polygon& p){
- ReversePolygon(p.x,p.y,p.nVertices);
- }
-
- void ReversePolygon(float* x, float* y, int n) {
- if (n == 1)
- return;
- int32 low = 0;
- int32 high = n - 1;
- while (low < high) {
- float32 buffer = x[low];
- x[low] = x[high];
- x[high] = buffer;
- buffer = y[low];
- y[low] = y[high];
- y[high] = buffer;
- ++low;
- --high;
- }
- }
- /**
- * Decomposes a non-convex polygon into a number of convex polygons, up
- * to maxPolys (remaining pieces are thrown out, but the total number
- * is returned, so the return value can be greater than maxPolys).
- *
- * Each resulting polygon will have no more than maxVerticesPerPolygon
- * vertices (set to b2MaxPolyVertices by default, though you can change
- * this).
- *
- * Returns -1 if operation fails (usually due to self-intersection of
- * polygon).
- */
- int32 DecomposeConvex(b2Polygon* p, b2Polygon* results, int32 maxPolys) {
- if (p->nVertices < 3) return 0;
- b2Triangle* triangulated = new b2Triangle[p->nVertices - 2];
- int32 nTri;
- if (p->IsCCW()) {
- //printf("It is ccw \n");
- b2Polygon tempP;
- tempP.Set(*p);
- ReversePolygon(tempP.x, tempP.y, tempP.nVertices);
- nTri = TriangulatePolygon(tempP.x, tempP.y, tempP.nVertices, triangulated);
- //ReversePolygon(p->x, p->y, p->nVertices); //reset orientation
- } else {
- //printf("It is not ccw \n");
- nTri = TriangulatePolygon(p->x, p->y, p->nVertices, triangulated);
- }
- if (nTri < 1) {
- //Still no luck? Oh well...
- delete[] triangulated;
- return -1;
- }
- int32 nPolys = PolygonizeTriangles(triangulated, nTri, results, maxPolys);
- delete[] triangulated;
- return nPolys;
- }
- /**
- * Decomposes a polygon into convex polygons and adds all pieces to a b2BodyDef
- * using a prototype b2PolyDef. All fields of the prototype are used for every
- * shape except the vertices (friction, restitution, density, etc).
- *
- * If you want finer control, you'll have to add everything by hand.
- *
- * This is the simplest method to add a complicated polygon to a body.
- *
- * Until Box2D's b2BodyDef behavior changes, this method returns a pointer to
- * a heap-allocated array of b2PolyDefs, which must be deleted by the user
- * after the b2BodyDef is added to the world.
- */
- #if 0
- void DecomposeConvexAndAddTo(b2Polygon* p, b2Body* bd, b2FixtureDef* prototype) {
- if (p->nVertices < 3) return;
- b2Polygon* decomposed = new b2Polygon[p->nVertices - 2]; //maximum number of polys
- int32 nPolys = DecomposeConvex(p, decomposed, p->nVertices - 2);
- //printf("npolys: %d",nPolys);
- b2FixtureDef* pdarray = new b2FixtureDef[2*p->nVertices];//extra space in case of splits
- int32 extra = 0;
- for (int32 i = 0; i < nPolys; ++i) {
- b2FixtureDef* toAdd = &pdarray[i+extra];
- *toAdd = *prototype;
- //Hmm, shouldn't have to do all this...
- /*
- toAdd->type = prototype->type;
- toAdd->friction = prototype->friction;
- toAdd->restitution = prototype->restitution;
- toAdd->density = prototype->density;
- toAdd->userData = prototype->userData;
- toAdd->categoryBits = prototype->categoryBits;
- toAdd->maskBits = prototype->maskBits;
- toAdd->groupIndex = prototype->groupIndex;//*/
- //decomposed[i].print();
- b2Polygon curr = decomposed[i];
- //TODO ewjordan: move this triangle handling to a better place so that
- //it happens even if this convenience function is not called.
- if (curr.nVertices == 3){
- //Check here for near-parallel edges, since we can't
- //handle this in merge routine
- for (int j=0; j<3; ++j){
- int32 lower = (j == 0) ? (curr.nVertices - 1) : (j - 1);
- int32 middle = j;
- int32 upper = (j == curr.nVertices - 1) ? (0) : (j + 1);
- float32 dx0 = curr.x[middle] - curr.x[lower]; float32 dy0 = curr.y[middle] - curr.y[lower];
- float32 dx1 = curr.x[upper] - curr.x[middle]; float32 dy1 = curr.y[upper] - curr.y[middle];
- float32 norm0 = sqrtf(dx0*dx0+dy0*dy0); float32 norm1 = sqrtf(dx1*dx1+dy1*dy1);
- if ( !(norm0 > 0.0f && norm1 > 0.0f) ) {
- //Identical points, don't do anything!
- goto Skip;
- }
- dx0 /= norm0; dy0 /= norm0;
- dx1 /= norm1; dy1 /= norm1;
- float32 cross = dx0 * dy1 - dx1 * dy0;
- float32 dot = dx0*dx1 + dy0*dy1;
- if (fabs(cross) < b2_angularSlop && dot > 0) {
- //Angle too close, split the triangle across from this point.
- //This is guaranteed to result in two triangles that satify
- //the tolerance (one of the angles is 90 degrees)
- float32 dx2 = curr.x[lower] - curr.x[upper]; float32 dy2 = curr.y[lower] - curr.y[upper];
- float32 norm2 = sqrtf(dx2*dx2+dy2*dy2);
- if (norm2 == 0.0f) {
- goto Skip;
- }
- dx2 /= norm2; dy2 /= norm2;
- float32 thisArea = curr.GetArea();
- float32 thisHeight = 2.0f * thisArea / norm2;
- float32 buffer2 = dx2;
- dx2 = dy2; dy2 = -buffer2;
- //Make two new polygons
- //printf("dx2: %f, dy2: %f, thisHeight: %f, middle: %d\n",dx2,dy2,thisHeight,middle);
- float32 newX1[3] = { curr.x[middle]+dx2*thisHeight, curr.x[lower], curr.x[middle] };
- float32 newY1[3] = { curr.y[middle]+dy2*thisHeight, curr.y[lower], curr.y[middle] };
- float32 newX2[3] = { newX1[0], curr.x[middle], curr.x[upper] };
- float32 newY2[3] = { newY1[0], curr.y[middle], curr.y[upper] };
- b2Polygon p1(newX1,newY1,3);
- b2Polygon p2(newX2,newY2,3);
- if (p1.IsUsable()){
- p1.AddTo(*toAdd);
-
-
- bd->CreateFixture(toAdd);
- ++extra;
- } else if (B2_POLYGON_REPORT_ERRORS){
- printf("Didn't add unusable polygon. Dumping vertices:\n");
- p1.print();
- }
- if (p2.IsUsable()){
- p2.AddTo(pdarray[i+extra]);
-
- bd->CreateFixture(toAdd);
- } else if (B2_POLYGON_REPORT_ERRORS){
- printf("Didn't add unusable polygon. Dumping vertices:\n");
- p2.print();
- }
- goto Skip;
- }
- }
- }
- if (decomposed[i].IsUsable()){
- decomposed[i].AddTo(*toAdd);
-
- bd->CreateFixture((const b2FixtureDef*)toAdd);
- } else if (B2_POLYGON_REPORT_ERRORS){
- printf("Didn't add unusable polygon. Dumping vertices:\n");
- decomposed[i].print();
- }
- Skip:
- ;
- }
- delete[] pdarray;
- delete[] decomposed;
- return;// pdarray; //needs to be deleted after body is created
- }
- #endif
- /**
- * Find the convex hull of a point cloud using "Gift-wrap" algorithm - start
- * with an extremal point, and walk around the outside edge by testing
- * angles.
- *
- * Runs in O(N*S) time where S is number of sides of resulting polygon.
- * Worst case: point cloud is all vertices of convex polygon -> O(N^2).
- *
- * There may be faster algorithms to do this, should you need one -
- * this is just the simplest. You can get O(N log N) expected time if you
- * try, I think, and O(N) if you restrict inputs to simple polygons.
- *
- * Returns null if number of vertices passed is less than 3.
- *
- * Results should be passed through convex decomposition afterwards
- * to ensure that each shape has few enough points to be used in Box2d.
- *
- * FIXME?: May be buggy with colinear points on hull. Couldn't find a test
- * case that resulted in wrong behavior. If one turns up, the solution is to
- * supplement angle check with an equality resolver that always picks the
- * longer edge. I think the current solution is working, though it sometimes
- * creates an extra edge along a line.
- */
-
- b2Polygon ConvexHull(b2Vec2* v, int nVert) {
- float32* cloudX = new float32[nVert];
- float32* cloudY = new float32[nVert];
- for (int32 i = 0; i < nVert; ++i) {
- cloudX[i] = v[i].x;
- cloudY[i] = v[i].y;
- }
- b2Polygon result = ConvexHull(cloudX, cloudY, nVert);
- delete[] cloudX;
- delete[] cloudY;
- return result;
- }
-
- b2Polygon ConvexHull(float32* cloudX, float32* cloudY, int32 nVert) {
- b2Assert(nVert > 2);
- int32* edgeList = new int32[nVert];
- int32 numEdges = 0;
-
- float32 minY = 1e10;
- int32 minYIndex = nVert;
- for (int32 i = 0; i < nVert; ++i) {
- if (cloudY[i] < minY) {
- minY = cloudY[i];
- minYIndex = i;
- }
- }
-
- int32 startIndex = minYIndex;
- int32 winIndex = -1;
- float32 dx = -1.0f;
- float32 dy = 0.0f;
- while (winIndex != minYIndex) {
- float32 newdx = 0.0f;
- float32 newdy = 0.0f;
- float32 maxDot = -2.0f;
- for (int32 i = 0; i < nVert; ++i) {
- if (i == startIndex)
- continue;
- newdx = cloudX[i] - cloudX[startIndex];
- newdy = cloudY[i] - cloudY[startIndex];
- float32 nrm = sqrtf(newdx * newdx + newdy * newdy);
- nrm = (nrm == 0.0f) ? 1.0f : nrm;
- newdx /= nrm;
- newdy /= nrm;
-
- //Cross and dot products act as proxy for angle
- //without requiring inverse trig.
- //FIXED: don't need cross test
- //float32 newCross = newdx * dy - newdy * dx;
- float32 newDot = newdx * dx + newdy * dy;
- if (newDot > maxDot) {//newCross >= 0.0f && newDot > maxDot) {
- maxDot = newDot;
- winIndex = i;
- }
- }
- edgeList[numEdges++] = winIndex;
- dx = cloudX[winIndex] - cloudX[startIndex];
- dy = cloudY[winIndex] - cloudY[startIndex];
- float32 nrm = sqrtf(dx * dx + dy * dy);
- nrm = (nrm == 0.0f) ? 1.0f : nrm;
- dx /= nrm;
- dy /= nrm;
- startIndex = winIndex;
- }
-
- float32* xres = new float32[numEdges];
- float32* yres = new float32[numEdges];
- for (int32 i = 0; i < numEdges; i++) {
- xres[i] = cloudX[edgeList[i]];
- yres[i] = cloudY[edgeList[i]];
- //("%f, %f\n",xres[i],yres[i]);
- }
-
- b2Polygon returnVal(xres, yres, numEdges);
- delete[] xres;
- delete[] yres;
- delete[] edgeList;
- returnVal.MergeParallelEdges(b2_angularSlop);
- return returnVal;
- }
- /*
- * Given sines and cosines, tells if A's angle is less than B's on -Pi, Pi
- * (in other words, is A "righter" than B)
- */
- bool IsRighter(float32 sinA, float32 cosA, float32 sinB, float32 cosB){
- if (sinA < 0){
- if (sinB > 0 || cosA <= cosB) return true;
- else return false;
- } else {
- if (sinB < 0 || cosA <= cosB) return false;
- else return true;
- }
- }
- //Fix for obnoxious behavior for the % operator for negative numbers...
- int32 remainder(int32 x, int32 modulus){
- int32 rem = x % modulus;
- while (rem < 0){
- rem += modulus;
- }
- return rem;
- }
- /*
- Method:
- Start at vertex with minimum y (pick maximum x one if there are two).
- We aim our "lastDir" vector at (1.0, 0)
- We look at the two rays going off from our start vertex, and follow whichever
- has the smallest angle (in -Pi -> Pi) wrt lastDir ("rightest" turn)
- Loop until we hit starting vertex:
- We add our current vertex to the list.
- We check the seg from current vertex to next vertex for intersections
- - if no intersections, follow to next vertex and continue
- - if intersections, pick one with minimum distance
- - if more than one, pick one with "rightest" next point (two possibilities for each)
- */
- b2Polygon TraceEdge(b2Polygon* p){
- b2PolyNode* nodes = new b2PolyNode[p->nVertices*p->nVertices];//overkill, but sufficient (order of mag. is right)
- int32 nNodes = 0;
- //Add base nodes (raw outline)
- for (int32 i=0; i < p->nVertices; ++i){
- b2Vec2 pos(p->x[i],p->y[i]);
- nodes[i].position = pos;
- ++nNodes;
- int32 iplus = (i==p->nVertices-1)?0:i+1;
- int32 iminus = (i==0)?p->nVertices-1:i-1;
- nodes[i].AddConnection(nodes[iplus]);
- nodes[i].AddConnection(nodes[iminus]);
- }
- //Process intersection nodes - horribly inefficient
- bool dirty = true;
- int counter = 0;
- while (dirty){
- dirty = false;
- for (int32 i=0; i < nNodes; ++i){
- for (int32 j=0; j < nodes[i].nConnected; ++j){
- for (int32 k=0; k < nNodes; ++k){
- if (k==i || &nodes[k] == nodes[i].connected[j]) continue;
- for (int32 l=0; l < nodes[k].nConnected; ++l){
-
- if ( nodes[k].connected[l] == nodes[i].connected[j] ||
- nodes[k].connected[l] == &nodes[i]) continue;
- //Check intersection
- b2Vec2 intersectPt;
- //if (counter > 100) printf("checking intersection: %d, %d, %d, %d\n",i,j,k,l);
- bool crosses = intersect(nodes[i].position,nodes[i].connected[j]->position,
- nodes[k].position,nodes[k].connected[l]->position,
- intersectPt);
- if (crosses){
- /*if (counter > 100) {
- printf("Found crossing at %f, %f\n",intersectPt.x, intersectPt.y);
- printf("Locations: %f,%f - %f,%f | %f,%f - %f,%f\n",
- nodes[i].position.x, nodes[i].position.y,
- nodes[i].connected[j]->position.x, nodes[i].connected[j]->position.y,
- nodes[k].position.x,nodes[k].position.y,
- nodes[k].connected[l]->position.x,nodes[k].connected[l]->position.y);
- printf("Memory addresses: %d, %d, %d, %d\n",(int)&nodes[i],(int)nodes[i].connected[j],(int)&nodes[k],(int)nodes[k].connected[l]);
- }*/
- dirty = true;
- //Destroy and re-hook connections at crossing point
- b2PolyNode* connj = nodes[i].connected[j];
- b2PolyNode* connl = nodes[k].connected[l];
- nodes[i].connected[j]->RemoveConnection(nodes[i]);
- nodes[i].RemoveConnection(*connj);
- nodes[k].connected[l]->RemoveConnection(nodes[k]);
- nodes[k].RemoveConnection(*connl);
- nodes[nNodes] = b2PolyNode(intersectPt);
- nodes[nNodes].AddConnection(nodes[i]);
- nodes[i].AddConnection(nodes[nNodes]);
- nodes[nNodes].AddConnection(nodes[k]);
- nodes[k].AddConnection(nodes[nNodes]);
- nodes[nNodes].AddConnection(*connj);
- connj->AddConnection(nodes[nNodes]);
- nodes[nNodes].AddConnection(*connl);
- connl->AddConnection(nodes[nNodes]);
- ++nNodes;
- goto SkipOut;
- }
- }
- }
- }
- }
- SkipOut:
- ++counter;
- //if (counter > 100) printf("Counter: %d\n",counter);
- }
-
- /*
- // Debugging: check for connection consistency
- for (int32 i=0; i<nNodes; ++i) {
- int32 nConn = nodes[i].nConnected;
- for (int32 j=0; j<nConn; ++j) {
- if (nodes[i].connected[j]->nConnected == 0) b2Assert(false);
- b2PolyNode* connect = nodes[i].connected[j];
- bool found = false;
- for (int32 k=0; k<connect->nConnected; ++k) {
- if (connect->connected[k] == &nodes[i]) found = true;
- }
- b2Assert(found);
- }
- }*/
- //Collapse duplicate points
- bool foundDupe = true;
- int nActive = nNodes;
- while (foundDupe){
- foundDupe = false;
- for (int32 i=0; i < nNodes; ++i){
- if (nodes[i].nConnected == 0) continue;
- for (int32 j=i+1; j < nNodes; ++j){
- if (nodes[j].nConnected == 0) continue;
- b2Vec2 diff = nodes[i].position - nodes[j].position;
- if (diff.LengthSquared() <= COLLAPSE_DIST_SQR){
- if (nActive <= 3) return b2Polygon();
- //printf("Found dupe, %d left\n",nActive);
- --nActive;
- foundDupe = true;
- b2PolyNode* inode = &nodes[i];
- b2PolyNode* jnode = &nodes[j];
- //Move all of j's connections to i, and orphan j
- int32 njConn = jnode->nConnected;
- for (int32 k=0; k < njConn; ++k){
- b2PolyNode* knode = jnode->connected[k];
- b2Assert(knode != jnode);
- if (knode != inode) {
- inode->AddConnection(*knode);
- knode->AddConnection(*inode);
- }
- knode->RemoveConnection(*jnode);
- //printf("knode %d on node %d now has %d connections\n",k,j,knode->nConnected);
- //printf("Found duplicate point.\n");
- }
- /*
- printf("Orphaning node at address %d\n",(int)jnode);
- for (int32 k=0; k<njConn; ++k) {
- if (jnode->connected[k]->IsConnectedTo(*jnode)) printf("Problem!!!\n");
- }
- for (int32 k=0; k < njConn; ++k){
- jnode->RemoveConnectionByIndex(k);
- }
- */
- jnode->nConnected = 0;
- }
- }
- }
- }
-
- /*
- // Debugging: check for connection consistency
- for (int32 i=0; i<nNodes; ++i) {
- int32 nConn = nodes[i].nConnected;
- printf("Node %d has %d connections\n",i,nConn);
- for (int32 j=0; j<nConn; ++j) {
- if (nodes[i].connected[j]->nConnected == 0) {
- printf("Problem with node %d connection at address %d\n",i,(int)(nodes[i].connected[j]));
- b2Assert(false);
- }
- b2PolyNode* connect = nodes[i].connected[j];
- bool found = false;
- for (int32 k=0; k<connect->nConnected; ++k) {
- if (connect->connected[k] == &nodes[i]) found = true;
- }
- if (!found) printf("Connection %d (of %d) on node %d (of %d) did not have reciprocal connection.\n",j,nConn,i,nNodes);
- b2Assert(found);
- }
- }//*/
- //Now walk the edge of the list
- //Find node with minimum y value (max x if equal)
- float32 minY = 1e10;
- float32 maxX = -1e10;
- int32 minYIndex = -1;
- for (int32 i = 0; i < nNodes; ++i) {
- if (nodes[i].position.y < minY && nodes[i].nConnected > 1) {
- minY = nodes[i].position.y;
- minYIndex = i;
- maxX = nodes[i].position.x;
- } else if (nodes[i].position.y == minY && nodes[i].position.x > maxX && nodes[i].nConnected > 1) {
- minYIndex = i;
- maxX = nodes[i].position.x;
- }
- }
- b2Vec2 origDir(1.0f,0.0f);
- b2Vec2* resultVecs = new b2Vec2[4*nNodes];// nodes may be visited more than once, unfortunately - change to growable array!
- int32 nResultVecs = 0;
- b2PolyNode* currentNode = &nodes[minYIndex];
- b2PolyNode* startNode = currentNode;
- b2Assert(currentNode->nConnected > 0);
- b2PolyNode* nextNode = currentNode->GetRightestConnection(origDir);
- if (!nextNode) goto CleanUp; // Borked, clean up our mess and return
- resultVecs[0] = startNode->position;
- ++nResultVecs;
- while (nextNode != startNode){
- if (nResultVecs > 4*nNodes){
- /*
- printf("%d, %d, %d\n",(int)startNode,(int)currentNode,(int)nextNode);
- printf("%f, %f -> %f, %f\n",currentNode->position.x,currentNode->position.y, nextNode->position.x, nextNode->position.y);
- p->printFormatted();
- printf("Dumping connection graph: \n");
- for (int32 i=0; i<nNodes; ++i) {
- printf("nodex[%d] = %f; nodey[%d] = %f;\n",i,nodes[i].position.x,i,nodes[i].position.y);
- printf("//connected to\n");
- for (int32 j=0; j<nodes[i].nConnected; ++j) {
- printf("connx[%d][%d] = %f; conny[%d][%d] = %f;\n",i,j,nodes[i].connected[j]->position.x, i,j,nodes[i].connected[j]->position.y);
- }
- }
- printf("Dumping results thus far: \n");
- for (int32 i=0; i<nResultVecs; ++i) {
- printf("x[%d]=map(%f,-3,3,0,width); y[%d] = map(%f,-3,3,height,0);\n",i,resultVecs[i].x,i,resultVecs[i].y);
- }
- //*/
- b2Assert(false); //nodes should never be visited four times apiece (proof?), so we've probably hit a loop...crap
- }
- resultVecs[nResultVecs++] = nextNode->position;
- b2PolyNode* oldNode = currentNode;
- currentNode = nextNode;
- //printf("Old node connections = %d; address %d\n",oldNode->nConnected, (int)oldNode);
- //printf("Current node connections = %d; address %d\n",currentNode->nConnected, (int)currentNode);
- nextNode = currentNode->GetRightestConnection(oldNode);
- if (!nextNode) goto CleanUp; // There was a problem, so jump out of the loop and use whatever garbage we've generated so far
- //printf("nextNode address: %d\n",(int)nextNode);
- }
- CleanUp:
-
- float32* xres = new float32[nResultVecs];
- float32* yres = new float32[nResultVecs];
- for (int32 i=0; i<nResultVecs; ++i){
- xres[i] = resultVecs[i].x;
- yres[i] = resultVecs[i].y;
- }
- b2Polygon retval(xres,yres,nResultVecs);
- delete[] resultVecs;
- delete[] yres;
- delete[] xres;
- delete[] nodes;
- return retval;
- }
- b2PolyNode::b2PolyNode(){
- nConnected = 0;
- visited = false;
- }
- b2PolyNode::b2PolyNode(b2Vec2& pos){
- position = pos;
- nConnected = 0;
- visited = false;
- }
- void b2PolyNode::AddConnection(b2PolyNode& toMe){
- b2Assert(nConnected < MAX_CONNECTED);
- // Ignore duplicate additions
- for (int32 i=0; i<nConnected; ++i) {
- if (connected[i] == &toMe) return;
- }
- connected[nConnected] = &toMe;
- ++nConnected;
- }
- void b2PolyNode::RemoveConnection(b2PolyNode& fromMe){
- bool isFound = false;
- int32 foundIndex = -1;
- for (int32 i=0; i<nConnected; ++i){
- if (&fromMe == connected[i]) {//.position == connected[i]->position){
- isFound = true;
- foundIndex = i;
- break;
- }
- }
- b2Assert(isFound);
- --nConnected;
- //printf("nConnected: %d\n",nConnected);
- for (int32 i=foundIndex; i < nConnected; ++i){
- connected[i] = connected[i+1];
- }
- }
- void b2PolyNode::RemoveConnectionByIndex(int32 index){
- --nConnected;
- //printf("New nConnected = %d\n",nConnected);
- for (int32 i=index; i < nConnected; ++i){
- connected[i] = connected[i+1];
- }
- }
- bool b2PolyNode::IsConnectedTo(b2PolyNode& me){
- bool isFound = false;
- for (int32 i=0; i<nConnected; ++i){
- if (&me == connected[i]) {//.position == connected[i]->position){
- isFound = true;
- break;
- }
- }
- return isFound;
- }
- b2PolyNode* b2PolyNode::GetRightestConnection(b2PolyNode* incoming){
- if (nConnected == 0) b2Assert(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;
- }
- b2Vec2 inDir = position - incoming->position;
- float32 inLength = inDir.Normalize();
- b2Assert(inLength > CMP_EPSILON);
-
- b2PolyNode* result = NULL;
- for (int32 i=0; i<nConnected; ++i){
- if (connected[i] == incoming) continue;
- b2Vec2 testDir = connected[i]->position - position;
- float32 testLengthSqr = testDir.LengthSquared();
- testDir.Normalize();
- /*
- if (testLengthSqr < COLLAPSE_DIST_SQR) {
- printf("Problem with connection %d\n",i);
- printf("This node has %d connections\n",nConnected);
- printf("That one has %d\n",connected[i]->nConnected);
- if (this == connected[i]) printf("This points at itself.\n");
- }*/
- b2Assert (testLengthSqr >= COLLAPSE_DIST_SQR);
- float32 myCos = b2Dot(inDir,testDir);
- float32 mySin = b2Cross(inDir,testDir);
- if (result){
- b2Vec2 resultDir = result->position - position;
- resultDir.Normalize();
- float32 resCos = b2Dot(inDir,resultDir);
- float32 resSin = b2Cross(inDir,resultDir);
- if (IsRighter(mySin,myCos,resSin,resCos)){
- result = connected[i];
- }
- } else{
- result = connected[i];
- }
- }
- if (B2_POLYGON_REPORT_ERRORS && !result) {
- printf("nConnected = %d\n",nConnected);
- for (int32 i=0; i<nConnected; ++i) {
- printf("connected[%d] @ %d\n",i,0);//(int)connected[i]);
- }
- }
- b2Assert(result);
- return result;
- }
- b2PolyNode* b2PolyNode::GetRightestConnection(b2Vec2& incomingDir){
- b2Vec2 diff = position-incomingDir;
- b2PolyNode temp(diff);
- b2PolyNode* res = GetRightestConnection(&temp);
- b2Assert(res);
- return res;
- }
- }
|