|
@@ -1,1591 +0,0 @@
|
|
|
-/*
|
|
|
- * 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) {
|
|
|
- // -- GODOT start --
|
|
|
- delete[] nodes;
|
|
|
- // -- GODOT end --
|
|
|
- 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;
|
|
|
-}
|
|
|
-}
|