├── README.md ├── b2Separator.cpp └── b2Separator.h /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # [b2Separator-cpp](https://github.com/delorenj/b2Separator-cpp) 4 | 5 | Create non-convex, complex shapes with Box2D. Ported from Antoan Angelov's b2Separator class. 6 | 7 | * Source: [https://github.com/delorenj/b2Separator-cpp](https://github.com/delorenj/b2Separator-cpp) 8 | 9 | ## Example Use 10 | 11 | b2Body *body; 12 | b2BodyDef *bodyDef = new b2BodyDef(); 13 | b2FixtureDef *fixtureDef = new b2FixtureDef(); 14 | b2Separator* sep = new b2Separator(); 15 | 16 | bodyDef->type = b2_dynamicBody; 17 | bodyDef->position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO); 18 | body = getWorld()->CreateBody(bodyDef); 19 | fixtureDef->restitution = 0.4f; 20 | fixtureDef->friction = 0.2f; 21 | fixtureDef->density = 4; 22 | 23 | vector* vec = new vector(); 24 | vec->push_back(b2Vec2(-3, -3)); 25 | vec->push_back(b2Vec2(3, -3)); 26 | vec->push_back(b2Vec2(3, 0)); 27 | vec->push_back(b2Vec2(0, 0)); 28 | vec->push_back(b2Vec2(-3, 3)); 29 | 30 | if(sep->Validate(*vec)==0) 31 | { 32 | CCLog("Yay! Those vertices are good to go!"); 33 | } 34 | else 35 | { 36 | CCLog("Oh, I guess you effed something up :("); 37 | } 38 | 39 | sep->Separate(body, fixtureDef, vec, PTM_RATIO); 40 | 41 | -------------------------------------------------------------------------------- /b2Separator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // b2Separator.cpp 3 | // Thermite 4 | // 5 | // Created by Jarad Delorenzo on 1/7/13. 6 | // 7 | // 8 | 9 | #include "b2Separator.h" 10 | #define MAX_VALUE 2147483647 11 | 12 | void b2Separator::Separate(b2Body* pBody, b2FixtureDef* pFixtureDef, vector* pVerticesVec, int scale) { 13 | int i, n=pVerticesVec->size(), j, m; 14 | vector vec; 15 | b2Vec2 *vertices; 16 | vector > figsVec; 17 | b2PolygonShape* polyShape = new b2PolygonShape(); 18 | 19 | for (i=0; iat(i).x*scale,pVerticesVec->at(i).y*scale)); 21 | } 22 | 23 | calcShapes(vec, figsVec); 24 | n = figsVec.size(); 25 | 26 | for (i=0; iSet(vertices, m); 34 | delete[] vertices; 35 | pFixtureDef->shape=polyShape; 36 | pBody->CreateFixture(pFixtureDef); 37 | } 38 | } 39 | /** 40 | * Checks whether the vertices in can be properly distributed into the new fixtures (more specifically, it makes sure there are no overlapping segments and the vertices are in clockwise order). 41 | * It is recommended that you use this method for debugging only, because it may cost more CPU usage. 42 | * @param verticesVec The vertices to be validated. 43 | * @return An integer which can have the following values: 44 | * 0 if the vertices can be properly processed. 45 | * 1 If there are overlapping lines. 46 | * 2 if the points are not in clockwise order. 47 | * 3 if there are overlapping lines and the points are not in clockwise order. 48 | * */ 49 | int b2Separator::Validate(const vector &verticesVec) { 50 | int i, n=verticesVec.size(), ret=0; 51 | float j, j2, i2, i3, d; 52 | bool fl, fl2=false; 53 | 54 | for (i=0; i0)?i-1:n-1; 57 | 58 | fl=false; 59 | for (j=0; j0)) { 64 | fl=true; 65 | } 66 | } 67 | 68 | if ((j!=i3)) { 69 | j2=(j &pVerticesVec, vector > &result) { 95 | vector vec; 96 | int i, n, j,minLen; 97 | float d, t, dx, dy; 98 | int i1, i2, i3; 99 | b2Vec2 p1, p2, p3; 100 | int j1, j2; 101 | b2Vec2 v1, v2; 102 | int k=0, h=0; 103 | vector *vec1, *vec2; 104 | b2Vec2 *pV, hitV(0,0); 105 | bool isConvex; 106 | vector > figsVec; 107 | queue > queue; 108 | 109 | queue.push(pVerticesVec); 110 | 111 | while (!queue.empty()) { 112 | vec = queue.front(); 113 | n = vec.size(); 114 | isConvex=true; 115 | 116 | for (i=0; i(); 161 | vec2 = new vector(); 162 | 163 | j1=h; 164 | j2=k; 165 | v1=vec[j1]; 166 | v2=vec[j2]; 167 | 168 | if (! pointsMatch(hitV.x,hitV.y,v2.x,v2.y)) { 169 | vec1->push_back(hitV); 170 | } 171 | if (! pointsMatch(hitV.x,hitV.y,v1.x,v1.y)) { 172 | vec2->push_back(hitV); 173 | } 174 | 175 | h=-1; 176 | k=i1; 177 | while (true) { 178 | if ((k!=j2)) { 179 | vec1->push_back(vec[k]); 180 | } 181 | else { 182 | if (((h<0)||h>=n)) { 183 | //TODO: Throw Error !!! 184 | } 185 | if (! isOnSegment(v2.x,v2.y,vec[h].x,vec[h].y,p1.x,p1.y)) { 186 | vec1->push_back(vec[k]); 187 | } 188 | break; 189 | } 190 | 191 | h=k; 192 | if (((k-1)<0)) { 193 | k=n-1; 194 | } 195 | else { 196 | k--; 197 | } 198 | } 199 | 200 | reverse(vec1->begin(), vec1->end()); 201 | 202 | h=-1; 203 | k=i2; 204 | while (true) { 205 | if ((k!=j1)) { 206 | vec2->push_back(vec[k]); 207 | } 208 | else { 209 | if (((h<0)||h>=n)) { 210 | //TODO: Throw Error !!! 211 | } 212 | if (((k==j1)&&! isOnSegment(v1.x,v1.y,vec[h].x,vec[h].y,p2.x,p2.y))) { 213 | vec2->push_back(vec[k]); 214 | } 215 | break; 216 | } 217 | 218 | h=k; 219 | if (((k+1)>n-1)) { 220 | k=0; 221 | } 222 | else { 223 | k++; 224 | } 225 | } 226 | 227 | queue.push(*vec1); 228 | queue.push(*vec2); 229 | queue.pop(); 230 | 231 | break; 232 | } 233 | } 234 | 235 | if (isConvex) { 236 | figsVec.push_back(queue.front()); 237 | queue.pop(); 238 | } 239 | } 240 | result = figsVec; 241 | } 242 | 243 | b2Vec2* b2Separator::hitRay(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { 244 | float t1 = x3-x1; 245 | float t2 = y3-y1; 246 | float t3 = x2-x1; 247 | float t4 = y2-y1; 248 | float t5 = x4-x3; 249 | float t6 = y4-y3; 250 | float t7 = t4*t5-t3*t6; 251 | 252 | //DBZ Error. Undefined hit segment. 253 | if(t7 == 0) return NULL; 254 | 255 | float a = (((t5*t2) - t6*t1) / t7); 256 | float px = x1+a*t3; 257 | float py = y1+a*t4; 258 | bool b1 = isOnSegment(x2,y2,x1,y1,px,py); 259 | bool b2 = isOnSegment(px,py,x3,y3,x4,y4); 260 | 261 | if(b1 && b2) { 262 | return new b2Vec2(px,py); 263 | } 264 | return NULL; 265 | } 266 | 267 | b2Vec2* b2Separator::hitSegment(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { 268 | float t1 = x3-x1; 269 | float t2 = y3-y1; 270 | float t3 = x2-x1; 271 | float t4 = y2-y1; 272 | float t5 = x4-x3; 273 | float t6 = y4-y3; 274 | float t7 = t4*t5 - t3*t6; 275 | 276 | //DBZ Error. Undefined hit segment. 277 | if(t7 == 0) return NULL; 278 | 279 | float a = (((t5*t2) - t6*t1) / t7); 280 | float px = x1+a*t3; 281 | float py = y1+a*t4; 282 | bool b1 = isOnSegment(px,py,x1,y1,x2,y2); 283 | bool b2 = isOnSegment(px,py,x3,y3,x4,y4); 284 | 285 | if(b1 && b2) { 286 | return new b2Vec2(px,py); 287 | } 288 | return NULL; 289 | } 290 | 291 | bool b2Separator::isOnSegment(float px, float py, float x1, float y1, float x2, float y2) { 292 | bool b1 = ((x1+0.1>=px&&px>=x2-0.1)||(x1-0.1<=px&&px<=x2+0.1)); 293 | bool b2 = ((y1+0.1>=py&&py>=y2-0.1)||(y1-0.1<=py&&py<=y2+0.1)); 294 | return (b1&&b2&&isOnLine(px, py, x1, y1, x2, y2)); 295 | } 296 | 297 | bool b2Separator::pointsMatch(float x1 ,float y1 ,float x2 ,float y2) { 298 | float dx = (x2>=x1) ? x2-x1 : x1-x2; 299 | float dy = (y2>=y1) ? y2-y1 : y1-y2; 300 | return ((dx<0.1f) && dy<0.1f); 301 | } 302 | 303 | bool b2Separator::isOnLine(float px ,float py ,float x1 ,float y1 ,float x2 ,float y2) { 304 | if(x2-x1>0.1f||x1-x2>0.1f) { 305 | float a=(y2-y1)/(x2-x1); 306 | float possibleY=a*(px-x1)+y1; 307 | float diff=(possibleY>py?possibleY-py:py-possibleY); 308 | return (diff<0.1f); 309 | } 310 | return (px-x1<0.1f||x1-px<0.1f); 311 | } 312 | 313 | float b2Separator::det(float x1, float y1, float x2, float y2, float x3, float y3) { 314 | return x1*y2+x2*y3+x3*y1-y1*x2-y2*x3-y3*x1; 315 | } 316 | 317 | // private function err():void { 318 | // throw new Error("A problem has occurred. Use the Validate() method to see where the problem is."); 319 | // } -------------------------------------------------------------------------------- /b2Separator.h: -------------------------------------------------------------------------------- 1 | // 2 | // b2Separator.h 3 | // Thermite 4 | // 5 | // Created by Jarad Delorenzo on 1/7/13. 6 | // 7 | // 8 | 9 | #ifndef __Thermite__b2Separator__ 10 | #define __Thermite__b2Separator__ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | /* 19 | * Convex Separator for Box2D Flash 20 | * 21 | * This class has been written by Antoan Angelov. 22 | * It is designed to work with Erin Catto's Box2D physics library. 23 | * 24 | * Everybody can use this software for any purpose, under two restrictions: 25 | * 1. You cannot claim that you wrote this software. 26 | * 2. You can not remove or alter this notice. 27 | * 28 | */ 29 | 30 | class b2Separator { 31 | 32 | public: 33 | 34 | b2Separator() {} 35 | 36 | /** 37 | * Separates a non-convex polygon into convex polygons and adds them as fixtures to the body parameter.
38 | * There are some rules you should follow (otherwise you might get unexpected results) : 39 | *
    40 | *
  • This class is specifically for non-convex polygons. If you want to create a convex polygon, you don't need to use this class - Box2D's b2PolygonShape class allows you to create convex shapes with the setAsArray()/setAsVector() method.
  • 41 | *
  • The vertices must be in clockwise order.
  • 42 | *
  • No three neighbouring points should lie on the same line segment.
  • 43 | *
  • There must be no overlapping segments and no "holes".
  • 44 | *

45 | * @param body The b2Body, in which the new fixtures will be stored. 46 | * @param fixtureDef A b2FixtureDef, containing all the properties (friction, density, etc.) which the new fixtures will inherit. 47 | * @param verticesVec The vertices of the non-convex polygon, in clockwise order. 48 | * @param scale [optional] The scale which you use to draw shapes in Box2D. The bigger the scale, the better the precision. The default value is 30. 49 | * @see b2PolygonShape 50 | * @see b2PolygonShape.SetAsArray() 51 | * @see b2PolygonShape.SetAsVector() 52 | * @see b2Fixture 53 | **/ 54 | 55 | void Separate(b2Body* pBody, b2FixtureDef* pFixtureDef, vector* pVerticesVec, int scale); 56 | /** 57 | * Checks whether the vertices in verticesVec can be properly distributed into the new fixtures (more specifically, it makes sure there are no overlapping segments and the vertices are in clockwise order). 58 | * It is recommended that you use this method for debugging only, because it may cost more CPU usage. 59 | *

60 | * @param verticesVec The vertices to be validated. 61 | * @return An integer which can have the following values: 62 | *

    63 | *
  • 0 if the vertices can be properly processed.
  • 64 | *
  • 1 If there are overlapping lines.
  • 65 | *
  • 2 if the points are not in clockwise order.
  • 66 | *
  • 3 if there are overlapping lines and the points are not in clockwise order.
  • 67 | *
68 | * */ 69 | 70 | int Validate(const vector& verticesVec); 71 | 72 | 73 | private: 74 | 75 | void calcShapes(vector &pVerticesVec, vector > &result); 76 | b2Vec2* hitRay(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); 77 | b2Vec2* hitSegment(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); 78 | bool isOnSegment(float px, float py, float x1, float y1, float x2, float y2); 79 | bool pointsMatch(float x1, float y1, float x2,float y2); 80 | bool isOnLine(float px, float py, float x1, float y1, float x2, float y2); 81 | float det( float x1, float y1, float x2, float y2, float x3, float y3); 82 | }; 83 | 84 | #endif /* defined(__Thermite__b2Separator__) */ 85 | --------------------------------------------------------------------------------