diff options
Diffstat (limited to 'thirdparty/b2d_convexdecomp/b2Polygon.cpp')
-rw-r--r-- | thirdparty/b2d_convexdecomp/b2Polygon.cpp | 1586 |
1 files changed, 1586 insertions, 0 deletions
diff --git a/thirdparty/b2d_convexdecomp/b2Polygon.cpp b/thirdparty/b2d_convexdecomp/b2Polygon.cpp new file mode 100644 index 0000000000..b6ead62c63 --- /dev/null +++ b/thirdparty/b2d_convexdecomp/b2Polygon.cpp @@ -0,0 +1,1586 @@ +/* + * 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; +} +} |