// QuickHull seemed like a neat algorithm, and efficient-ish for large input sets. // My implementation performs an in place reduction using the result array as scratch space. int cpConvexHull(int count, cpVect *verts, cpVect *result, int *first, cpFloat tol) { if(result){ // Copy the line vertexes into the empty part of the result polyline to use as a scratch buffer. memcpy(result, verts, count*sizeof(cpVect)); } else { // If a result array was not specified, reduce the input instead. result = verts; } // Degenerate case, all poins are the same. int start, end; cpLoopIndexes(verts, count, &start, &end); if(start == end){ if(first) (*first) = 0; return 1; } SWAP(result[0], result[start]); SWAP(result[1], result[end == 0 ? start : end]); cpVect a = result[0]; cpVect b = result[1]; if(first) (*first) = start; int resultCount = QHullReduce(tol, result + 2, count - 2, a, b, a, result + 1) + 1; cpAssertSoft(cpPolyValidate(result, resultCount), "Internal error: cpConvexHull() and cpPolyValidate() did not agree." "Please report this error with as much info as you can."); return resultCount; }
static void setUpVerts(cpPolyShape *poly, int numVerts, const cpVect *verts, cpVect offset) { // Fail if the user attempts to pass a concave poly, or a bad winding. cpAssertHard(cpPolyValidate(verts, numVerts), "Polygon is concave or has a reversed winding. Consider using cpConvexHull() or CP_CONVEX_HULL()."); poly->numVerts = numVerts; poly->verts = (cpVect *)cpcalloc(2*numVerts, sizeof(cpVect)); poly->planes = (cpSplittingPlane *)cpcalloc(2*numVerts, sizeof(cpSplittingPlane)); poly->tVerts = poly->verts + numVerts; poly->tPlanes = poly->planes + numVerts; for(int i=0; i<numVerts; i++){ cpVect a = cpvadd(offset, verts[i]); cpVect b = cpvadd(offset, verts[(i+1)%numVerts]); cpVect n = cpvnormalize(cpvperp(cpvsub(b, a))); poly->verts[i] = a; poly->planes[i].n = n; poly->planes[i].d = cpvdot(n, a); } // TODO: Why did I add this? It duplicates work from above. for(int i=0; i<numVerts; i++){ poly->planes[i] = cpSplittingPlaneNew(poly->verts[(i - 1 + numVerts)%numVerts], poly->verts[i]); } }
cpPolyShape * cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int numVerts, cpVect *verts, cpVect offset) { // Fail if the user attempts to pass a concave poly, or a bad winding. assert(cpPolyValidate(verts, numVerts)); setUpVerts(poly, numVerts, verts, offset); cpShapeInit((cpShape *)poly, &polyClass, body); return poly; }
/** @name areaForCircle @text Returns the area for a polygon. @in table vertices Array containg vertex coordinate components ( t[1] = x0, t[2] = y0, t[3] = x1, t[4] = y1... ) @out number area */ int MOAICpShape::_areaForPolygon ( lua_State* L ) { USLuaState state ( L ); if ( !state.CheckParams ( 1, "T" )) return 0; cpVect verts [ MAX_POLY_VERTS ]; int numVerts = MOAICpShape::LoadVerts ( state, 1, verts, MAX_POLY_VERTS ); if ( numVerts && cpPolyValidate ( verts, numVerts )) { cpFloat area = cpAreaForPoly ( numVerts, verts ); area = area < 0 ? -area : area; lua_pushnumber ( L, area ); return 1; } return 0; }