chiphunk (empty) → 0.1.0.0
raw patch · 81 files changed
+18438/−0 lines, 81 filesdep +StateVardep +asyncdep +basesetup-changed
Dependencies added: StateVar, async, base, chiphunk, nanovg, nanovg-simple, safe-exceptions, vector-space
Files
- Chipmunk2D-7.0.2/include/chipmunk/chipmunk.h +232/−0
- Chipmunk2D-7.0.2/include/chipmunk/chipmunk_ffi.h +105/−0
- Chipmunk2D-7.0.2/include/chipmunk/chipmunk_private.h +344/−0
- Chipmunk2D-7.0.2/include/chipmunk/chipmunk_structs.h +450/−0
- Chipmunk2D-7.0.2/include/chipmunk/chipmunk_types.h +268/−0
- Chipmunk2D-7.0.2/include/chipmunk/chipmunk_unsafe.h +66/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpArbiter.h +145/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpBB.h +187/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpBody.h +189/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpConstraint.h +95/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpDampedRotarySpring.h +58/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpDampedSpring.h +68/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpGearJoint.h +45/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpGrooveJoint.h +50/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpHastySpace.h +27/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpMarch.h +28/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpPinJoint.h +50/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpPivotJoint.h +47/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpPolyShape.h +56/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpPolyline.h +70/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpRatchetJoint.h +50/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpRobust.h +11/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpRotaryLimitJoint.h +45/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpShape.h +199/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpSimpleMotor.h +43/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpSlideJoint.h +55/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpSpace.h +319/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpSpatialIndex.h +227/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpTransform.h +198/−0
- Chipmunk2D-7.0.2/include/chipmunk/cpVect.h +230/−0
- Chipmunk2D-7.0.2/src/chipmunk.c +331/−0
- Chipmunk2D-7.0.2/src/cpArbiter.c +496/−0
- Chipmunk2D-7.0.2/src/cpArray.c +101/−0
- Chipmunk2D-7.0.2/src/cpBBTree.c +896/−0
- Chipmunk2D-7.0.2/src/cpBody.c +626/−0
- Chipmunk2D-7.0.2/src/cpCollision.c +726/−0
- Chipmunk2D-7.0.2/src/cpConstraint.c +173/−0
- Chipmunk2D-7.0.2/src/cpDampedRotarySpring.c +178/−0
- Chipmunk2D-7.0.2/src/cpDampedSpring.c +216/−0
- Chipmunk2D-7.0.2/src/cpGearJoint.c +145/−0
- Chipmunk2D-7.0.2/src/cpGrooveJoint.c +197/−0
- Chipmunk2D-7.0.2/src/cpHashSet.c +253/−0
- Chipmunk2D-7.0.2/src/cpHastySpace.c +694/−0
- Chipmunk2D-7.0.2/src/cpMarch.c +157/−0
- Chipmunk2D-7.0.2/src/cpPinJoint.c +172/−0
- Chipmunk2D-7.0.2/src/cpPivotJoint.c +152/−0
- Chipmunk2D-7.0.2/src/cpPolyShape.c +323/−0
- Chipmunk2D-7.0.2/src/cpPolyline.c +652/−0
- Chipmunk2D-7.0.2/src/cpRatchetJoint.c +179/−0
- Chipmunk2D-7.0.2/src/cpRobust.c +13/−0
- Chipmunk2D-7.0.2/src/cpRotaryLimitJoint.c +160/−0
- Chipmunk2D-7.0.2/src/cpShape.c +603/−0
- Chipmunk2D-7.0.2/src/cpSimpleMotor.c +123/−0
- Chipmunk2D-7.0.2/src/cpSlideJoint.c +195/−0
- Chipmunk2D-7.0.2/src/cpSpace.c +700/−0
- Chipmunk2D-7.0.2/src/cpSpaceComponent.c +349/−0
- Chipmunk2D-7.0.2/src/cpSpaceDebug.c +189/−0
- Chipmunk2D-7.0.2/src/cpSpaceHash.c +634/−0
- Chipmunk2D-7.0.2/src/cpSpaceQuery.c +246/−0
- Chipmunk2D-7.0.2/src/cpSpaceStep.c +445/−0
- Chipmunk2D-7.0.2/src/cpSpatialIndex.c +69/−0
- Chipmunk2D-7.0.2/src/cpSweep1D.c +254/−0
- LICENSE +30/−0
- README.md +3/−0
- Setup.hs +2/−0
- app/Main.hs +168/−0
- chiphunk.cabal +137/−0
- src/Chiphunk/Low.hs +998/−0
- src/Chiphunk/Low/Arbiter.chs +172/−0
- src/Chiphunk/Low/BB.chs +128/−0
- src/Chiphunk/Low/Body.chs +319/−0
- src/Chiphunk/Low/Callback.chs +159/−0
- src/Chiphunk/Low/Constraint.chs +467/−0
- src/Chiphunk/Low/Helper.chs +99/−0
- src/Chiphunk/Low/Internal.hs +9/−0
- src/Chiphunk/Low/Math.hs +32/−0
- src/Chiphunk/Low/Shape.chs +226/−0
- src/Chiphunk/Low/Space.chs +266/−0
- src/Chiphunk/Low/Types.chs +224/−0
- src/Chiphunk/Low/Vect.chs +219/−0
- src/Chiphunk/wrapper.c +176/−0
+ Chipmunk2D-7.0.2/include/chipmunk/chipmunk.h view
@@ -0,0 +1,232 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#ifndef CHIPMUNK_H+#define CHIPMUNK_H++#include <stdlib.h>+#include <math.h>++#ifndef alloca+ #ifdef _WIN32+ #include <malloc.h>+ #elif defined(__FreeBSD__)+ /* already included in <stdlib.h> */+ #else+ #include <alloca.h>+ #endif+#endif++#ifdef _WIN32+ #define CP_EXPORT __declspec(dllexport)+#else+ #define CP_EXPORT+#endif++#ifdef __cplusplus+extern "C" {+#endif++CP_EXPORT void cpMessage(const char *condition, const char *file, int line, int isError, int isHardError, const char *message, ...);+#ifdef NDEBUG+ #define cpAssertWarn(__condition__, ...)+ #define cpAssertSoft(__condition__, ...)+#else+ #define cpAssertSoft(__condition__, ...) if(!(__condition__)){cpMessage(#__condition__, __FILE__, __LINE__, 1, 0, __VA_ARGS__); abort();}+ #define cpAssertWarn(__condition__, ...) if(!(__condition__)) cpMessage(#__condition__, __FILE__, __LINE__, 0, 0, __VA_ARGS__)+#endif++// Hard assertions are used in situations where the program definitely will crash anyway, and the reason is inexpensive to detect.+#define cpAssertHard(__condition__, ...) if(!(__condition__)){cpMessage(#__condition__, __FILE__, __LINE__, 1, 1, __VA_ARGS__); abort();}++#include "chipmunk_types.h"+ +/// @defgroup misc Misc+/// @{++/// Allocated size for various Chipmunk buffers+#ifndef CP_BUFFER_BYTES+ #define CP_BUFFER_BYTES (32*1024)+#endif++#ifndef cpcalloc+ /// Chipmunk calloc() alias.+ #define cpcalloc calloc+#endif++#ifndef cprealloc+ /// Chipmunk realloc() alias.+ #define cprealloc realloc+#endif++#ifndef cpfree+ /// Chipmunk free() alias.+ #define cpfree free+#endif++typedef struct cpArray cpArray;+typedef struct cpHashSet cpHashSet;++typedef struct cpBody cpBody;++typedef struct cpShape cpShape;+typedef struct cpCircleShape cpCircleShape;+typedef struct cpSegmentShape cpSegmentShape;+typedef struct cpPolyShape cpPolyShape;++typedef struct cpConstraint cpConstraint;+typedef struct cpPinJoint cpPinJoint;+typedef struct cpSlideJoint cpSlideJoint;+typedef struct cpPivotJoint cpPivotJoint;+typedef struct cpGrooveJoint cpGrooveJoint;+typedef struct cpDampedSpring cpDampedSpring;+typedef struct cpDampedRotarySpring cpDampedRotarySpring;+typedef struct cpRotaryLimitJoint cpRotaryLimitJoint;+typedef struct cpRatchetJoint cpRatchetJoint;+typedef struct cpGearJoint cpGearJoint;+typedef struct cpSimpleMotorJoint cpSimpleMotorJoint;++typedef struct cpCollisionHandler cpCollisionHandler;+typedef struct cpContactPointSet cpContactPointSet;+typedef struct cpArbiter cpArbiter;++typedef struct cpSpace cpSpace;++#include "cpVect.h"+#include "cpBB.h"+#include "cpTransform.h"+#include "cpSpatialIndex.h"++#include "cpArbiter.h" ++#include "cpBody.h"+#include "cpShape.h"+#include "cpPolyShape.h"++#include "cpConstraint.h"++#include "cpSpace.h"++// Chipmunk 7.0.2+#define CP_VERSION_MAJOR 7+#define CP_VERSION_MINOR 0+#define CP_VERSION_RELEASE 2++/// Version string.+CP_EXPORT extern const char *cpVersionString;++/// Calculate the moment of inertia for a circle.+/// @c r1 and @c r2 are the inner and outer diameters. A solid circle has an inner diameter of 0.+CP_EXPORT cpFloat cpMomentForCircle(cpFloat m, cpFloat r1, cpFloat r2, cpVect offset);++/// Calculate area of a hollow circle.+/// @c r1 and @c r2 are the inner and outer diameters. A solid circle has an inner diameter of 0.+CP_EXPORT cpFloat cpAreaForCircle(cpFloat r1, cpFloat r2);++/// Calculate the moment of inertia for a line segment.+/// Beveling radius is not supported.+CP_EXPORT cpFloat cpMomentForSegment(cpFloat m, cpVect a, cpVect b, cpFloat radius);++/// Calculate the area of a fattened (capsule shaped) line segment.+CP_EXPORT cpFloat cpAreaForSegment(cpVect a, cpVect b, cpFloat radius);++/// Calculate the moment of inertia for a solid polygon shape assuming it's center of gravity is at it's centroid. The offset is added to each vertex.+CP_EXPORT cpFloat cpMomentForPoly(cpFloat m, int count, const cpVect *verts, cpVect offset, cpFloat radius);++/// Calculate the signed area of a polygon. A Clockwise winding gives positive area.+/// This is probably backwards from what you expect, but matches Chipmunk's the winding for poly shapes.+CP_EXPORT cpFloat cpAreaForPoly(const int count, const cpVect *verts, cpFloat radius);++/// Calculate the natural centroid of a polygon.+CP_EXPORT cpVect cpCentroidForPoly(const int count, const cpVect *verts);++/// Calculate the moment of inertia for a solid box.+CP_EXPORT cpFloat cpMomentForBox(cpFloat m, cpFloat width, cpFloat height);++/// Calculate the moment of inertia for a solid box.+CP_EXPORT cpFloat cpMomentForBox2(cpFloat m, cpBB box);++/// Calculate the convex hull of a given set of points. Returns the count of points in the hull.+/// @c result must be a pointer to a @c cpVect array with at least @c count elements. If @c verts == @c result, then @c verts will be reduced inplace.+/// @c first is an optional pointer to an integer to store where the first vertex in the hull came from (i.e. verts[first] == result[0])+/// @c tol is the allowed amount to shrink the hull when simplifying it. A tolerance of 0.0 creates an exact hull.+CP_EXPORT int cpConvexHull(int count, const cpVect *verts, cpVect *result, int *first, cpFloat tol);++/// Convenience macro to work with cpConvexHull.+/// @c count and @c verts is the input array passed to cpConvexHull().+/// @c count_var and @c verts_var are the names of the variables the macro creates to store the result.+/// The output vertex array is allocated on the stack using alloca() so it will be freed automatically, but cannot be returned from the current scope.+#define CP_CONVEX_HULL(__count__, __verts__, __count_var__, __verts_var__) \+cpVect *__verts_var__ = (cpVect *)alloca(__count__*sizeof(cpVect)); \+int __count_var__ = cpConvexHull(__count__, __verts__, __verts_var__, NULL, 0.0); \++/// Returns the closest point on the line segment ab, to the point p.+static inline cpVect+cpClosetPointOnSegment(const cpVect p, const cpVect a, const cpVect b)+{+ cpVect delta = cpvsub(a, b);+ cpFloat t = cpfclamp01(cpvdot(delta, cpvsub(p, b))/cpvlengthsq(delta));+ return cpvadd(b, cpvmult(delta, t));+}++#if defined(__has_extension)+#if __has_extension(blocks)+// Define alternate block based alternatives for a few of the callback heavy functions.+// Collision handlers are post-step callbacks are not included to avoid memory management issues.+// If you want to use blocks for those and are aware of how to correctly manage the memory, the implementation is trivial. ++void cpSpaceEachBody_b(cpSpace *space, void (^block)(cpBody *body));+void cpSpaceEachShape_b(cpSpace *space, void (^block)(cpShape *shape));+void cpSpaceEachConstraint_b(cpSpace *space, void (^block)(cpConstraint *constraint));++void cpBodyEachShape_b(cpBody *body, void (^block)(cpShape *shape));+void cpBodyEachConstraint_b(cpBody *body, void (^block)(cpConstraint *constraint));+void cpBodyEachArbiter_b(cpBody *body, void (^block)(cpArbiter *arbiter));++typedef void (^cpSpacePointQueryBlock)(cpShape *shape, cpVect point, cpFloat distance, cpVect gradient);+void cpSpacePointQuery_b(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryBlock block);++typedef void (^cpSpaceSegmentQueryBlock)(cpShape *shape, cpVect point, cpVect normal, cpFloat alpha);+void cpSpaceSegmentQuery_b(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryBlock block);++typedef void (^cpSpaceBBQueryBlock)(cpShape *shape);+void cpSpaceBBQuery_b(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryBlock block);++typedef void (^cpSpaceShapeQueryBlock)(cpShape *shape, cpContactPointSet *points);+cpBool cpSpaceShapeQuery_b(cpSpace *space, cpShape *shape, cpSpaceShapeQueryBlock block);++#endif+#endif+++//@}++#ifdef __cplusplus+}++static inline cpVect operator *(const cpVect v, const cpFloat s){return cpvmult(v, s);}+static inline cpVect operator +(const cpVect v1, const cpVect v2){return cpvadd(v1, v2);}+static inline cpVect operator -(const cpVect v1, const cpVect v2){return cpvsub(v1, v2);}+static inline cpBool operator ==(const cpVect v1, const cpVect v2){return cpveql(v1, v2);}+static inline cpVect operator -(const cpVect v){return cpvneg(v);}++#endif+#endif
+ Chipmunk2D-7.0.2/include/chipmunk/chipmunk_ffi.h view
@@ -0,0 +1,105 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#ifdef CHIPMUNK_FFI++// Create non static inlined copies of Chipmunk functions, useful for working with dynamic FFIs+// For many languages, it may be faster to reimplement these functions natively instead.+// Note: This file should only be included by chipmunk.c.++#ifdef _MSC_VER+ #if _MSC_VER >= 1600+ #define MAKE_REF(name) CP_EXPORT decltype(name) *_##name = name+ #else+ #define MAKE_REF(name)+ #endif+#else+ #define MAKE_REF(name) __typeof__(name) *_##name = name+#endif++#ifdef __cplusplus+extern "C" {+#endif++MAKE_REF(cpv); // makes a variable named _cpv that contains the function pointer for cpv()+MAKE_REF(cpveql);+MAKE_REF(cpvadd);+MAKE_REF(cpvneg);+MAKE_REF(cpvsub);+MAKE_REF(cpvmult);+MAKE_REF(cpvdot);+MAKE_REF(cpvcross);+MAKE_REF(cpvperp);+MAKE_REF(cpvrperp);+MAKE_REF(cpvproject);+MAKE_REF(cpvforangle);+MAKE_REF(cpvtoangle);+MAKE_REF(cpvrotate);+MAKE_REF(cpvunrotate);+MAKE_REF(cpvlengthsq);+MAKE_REF(cpvlength);+MAKE_REF(cpvlerp);+MAKE_REF(cpvnormalize);+MAKE_REF(cpvclamp);+MAKE_REF(cpvlerpconst);+MAKE_REF(cpvdist);+MAKE_REF(cpvdistsq);+MAKE_REF(cpvnear);++MAKE_REF(cpfmax);+MAKE_REF(cpfmin);+MAKE_REF(cpfabs);+MAKE_REF(cpfclamp);+MAKE_REF(cpflerp);+MAKE_REF(cpflerpconst);++MAKE_REF(cpBBNew);+MAKE_REF(cpBBNewForExtents);+MAKE_REF(cpBBNewForCircle);+MAKE_REF(cpBBIntersects);+MAKE_REF(cpBBContainsBB);+MAKE_REF(cpBBContainsVect);+MAKE_REF(cpBBMerge);+MAKE_REF(cpBBExpand);+MAKE_REF(cpBBCenter);+MAKE_REF(cpBBArea);+MAKE_REF(cpBBMergedArea);+MAKE_REF(cpBBSegmentQuery);+MAKE_REF(cpBBIntersectsSegment);+MAKE_REF(cpBBClampVect);++MAKE_REF(cpSpatialIndexDestroy);+MAKE_REF(cpSpatialIndexCount);+MAKE_REF(cpSpatialIndexEach);+MAKE_REF(cpSpatialIndexContains);+MAKE_REF(cpSpatialIndexInsert);+MAKE_REF(cpSpatialIndexRemove);+MAKE_REF(cpSpatialIndexReindex);+MAKE_REF(cpSpatialIndexReindexObject);+MAKE_REF(cpSpatialIndexSegmentQuery);+MAKE_REF(cpSpatialIndexQuery);+MAKE_REF(cpSpatialIndexReindexQuery);++#ifdef __cplusplus+}+#endif++#endif
+ Chipmunk2D-7.0.2/include/chipmunk/chipmunk_private.h view
@@ -0,0 +1,344 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#ifndef CHIPMUNK_PRIVATE_H+#define CHIPMUNK_PRIVATE_H++#include "chipmunk/chipmunk.h"+#include "chipmunk/chipmunk_structs.h"++#define CP_HASH_COEF (3344921057ul)+#define CP_HASH_PAIR(A, B) ((cpHashValue)(A)*CP_HASH_COEF ^ (cpHashValue)(B)*CP_HASH_COEF)++// TODO: Eww. Magic numbers.+#define MAGIC_EPSILON 1e-5+++//MARK: cpArray++cpArray *cpArrayNew(int size);++void cpArrayFree(cpArray *arr);++void cpArrayPush(cpArray *arr, void *object);+void *cpArrayPop(cpArray *arr);+void cpArrayDeleteObj(cpArray *arr, void *obj);+cpBool cpArrayContains(cpArray *arr, void *ptr);++void cpArrayFreeEach(cpArray *arr, void (freeFunc)(void*));+++//MARK: cpHashSet++typedef cpBool (*cpHashSetEqlFunc)(void *ptr, void *elt);+typedef void *(*cpHashSetTransFunc)(void *ptr, void *data);++cpHashSet *cpHashSetNew(int size, cpHashSetEqlFunc eqlFunc);+void cpHashSetSetDefaultValue(cpHashSet *set, void *default_value);++void cpHashSetFree(cpHashSet *set);++int cpHashSetCount(cpHashSet *set);+void *cpHashSetInsert(cpHashSet *set, cpHashValue hash, void *ptr, cpHashSetTransFunc trans, void *data);+void *cpHashSetRemove(cpHashSet *set, cpHashValue hash, void *ptr);+void *cpHashSetFind(cpHashSet *set, cpHashValue hash, void *ptr);++typedef void (*cpHashSetIteratorFunc)(void *elt, void *data);+void cpHashSetEach(cpHashSet *set, cpHashSetIteratorFunc func, void *data);++typedef cpBool (*cpHashSetFilterFunc)(void *elt, void *data);+void cpHashSetFilter(cpHashSet *set, cpHashSetFilterFunc func, void *data);+++//MARK: Bodies++void cpBodyAddShape(cpBody *body, cpShape *shape);+void cpBodyRemoveShape(cpBody *body, cpShape *shape);++//void cpBodyAccumulateMassForShape(cpBody *body, cpShape *shape);+void cpBodyAccumulateMassFromShapes(cpBody *body);++void cpBodyRemoveConstraint(cpBody *body, cpConstraint *constraint);+++//MARK: Spatial Index Functions++cpSpatialIndex *cpSpatialIndexInit(cpSpatialIndex *index, cpSpatialIndexClass *klass, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);+++//MARK: Arbiters++cpArbiter* cpArbiterInit(cpArbiter *arb, cpShape *a, cpShape *b);++static inline struct cpArbiterThread *+cpArbiterThreadForBody(cpArbiter *arb, cpBody *body)+{+ return (arb->body_a == body ? &arb->thread_a : &arb->thread_b);+}++void cpArbiterUnthread(cpArbiter *arb);++void cpArbiterUpdate(cpArbiter *arb, struct cpCollisionInfo *info, cpSpace *space);+void cpArbiterPreStep(cpArbiter *arb, cpFloat dt, cpFloat bias, cpFloat slop);+void cpArbiterApplyCachedImpulse(cpArbiter *arb, cpFloat dt_coef);+void cpArbiterApplyImpulse(cpArbiter *arb);+++//MARK: Shapes/Collisions++cpShape *cpShapeInit(cpShape *shape, const cpShapeClass *klass, cpBody *body, struct cpShapeMassInfo massInfo);++static inline cpBool+cpShapeActive(cpShape *shape)+{+ // checks if the shape is added to a shape list.+ // TODO could this just check the space now?+ return (shape->prev || (shape->body && shape->body->shapeList == shape));+}++// Note: This function returns contact points with r1/r2 in absolute coordinates, not body relative.+struct cpCollisionInfo cpCollide(const cpShape *a, const cpShape *b, cpCollisionID id, struct cpContact *contacts);++static inline void+CircleSegmentQuery(cpShape *shape, cpVect center, cpFloat r1, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info)+{+ cpVect da = cpvsub(a, center);+ cpVect db = cpvsub(b, center);+ cpFloat rsum = r1 + r2;+ + cpFloat qa = cpvdot(da, da) - 2.0f*cpvdot(da, db) + cpvdot(db, db);+ cpFloat qb = cpvdot(da, db) - cpvdot(da, da);+ cpFloat det = qb*qb - qa*(cpvdot(da, da) - rsum*rsum);+ + if(det >= 0.0f){+ cpFloat t = (-qb - cpfsqrt(det))/(qa);+ if(0.0f<= t && t <= 1.0f){+ cpVect n = cpvnormalize(cpvlerp(da, db, t));+ + info->shape = shape;+ info->point = cpvsub(cpvlerp(a, b, t), cpvmult(n, r2));+ info->normal = n;+ info->alpha = t;+ }+ }+}++static inline cpBool+cpShapeFilterReject(cpShapeFilter a, cpShapeFilter b)+{+ // Reject the collision if:+ return (+ // They are in the same non-zero group.+ (a.group != 0 && a.group == b.group) ||+ // One of the category/mask combinations fails.+ (a.categories & b.mask) == 0 ||+ (b.categories & a.mask) == 0+ );+}++void cpLoopIndexes(const cpVect *verts, int count, int *start, int *end);+++//MARK: Constraints+// TODO naming conventions here++void cpConstraintInit(cpConstraint *constraint, const struct cpConstraintClass *klass, cpBody *a, cpBody *b);++static inline void+cpConstraintActivateBodies(cpConstraint *constraint)+{+ cpBody *a = constraint->a; cpBodyActivate(a);+ cpBody *b = constraint->b; cpBodyActivate(b);+}++static inline cpVect+relative_velocity(cpBody *a, cpBody *b, cpVect r1, cpVect r2){+ cpVect v1_sum = cpvadd(a->v, cpvmult(cpvperp(r1), a->w));+ cpVect v2_sum = cpvadd(b->v, cpvmult(cpvperp(r2), b->w));+ + return cpvsub(v2_sum, v1_sum);+}++static inline cpFloat+normal_relative_velocity(cpBody *a, cpBody *b, cpVect r1, cpVect r2, cpVect n){+ return cpvdot(relative_velocity(a, b, r1, r2), n);+}++static inline void+apply_impulse(cpBody *body, cpVect j, cpVect r){+ body->v = cpvadd(body->v, cpvmult(j, body->m_inv));+ body->w += body->i_inv*cpvcross(r, j);+}++static inline void+apply_impulses(cpBody *a , cpBody *b, cpVect r1, cpVect r2, cpVect j)+{+ apply_impulse(a, cpvneg(j), r1);+ apply_impulse(b, j, r2);+}++static inline void+apply_bias_impulse(cpBody *body, cpVect j, cpVect r)+{+ body->v_bias = cpvadd(body->v_bias, cpvmult(j, body->m_inv));+ body->w_bias += body->i_inv*cpvcross(r, j);+}++static inline void+apply_bias_impulses(cpBody *a , cpBody *b, cpVect r1, cpVect r2, cpVect j)+{+ apply_bias_impulse(a, cpvneg(j), r1);+ apply_bias_impulse(b, j, r2);+}++static inline cpFloat+k_scalar_body(cpBody *body, cpVect r, cpVect n)+{+ cpFloat rcn = cpvcross(r, n);+ return body->m_inv + body->i_inv*rcn*rcn;+}++static inline cpFloat+k_scalar(cpBody *a, cpBody *b, cpVect r1, cpVect r2, cpVect n)+{+ cpFloat value = k_scalar_body(a, r1, n) + k_scalar_body(b, r2, n);+ cpAssertSoft(value != 0.0, "Unsolvable collision or constraint.");+ + return value;+}++static inline cpMat2x2+k_tensor(cpBody *a, cpBody *b, cpVect r1, cpVect r2)+{+ cpFloat m_sum = a->m_inv + b->m_inv;+ + // start with Identity*m_sum+ cpFloat k11 = m_sum, k12 = 0.0f;+ cpFloat k21 = 0.0f, k22 = m_sum;+ + // add the influence from r1+ cpFloat a_i_inv = a->i_inv;+ cpFloat r1xsq = r1.x * r1.x * a_i_inv;+ cpFloat r1ysq = r1.y * r1.y * a_i_inv;+ cpFloat r1nxy = -r1.x * r1.y * a_i_inv;+ k11 += r1ysq; k12 += r1nxy;+ k21 += r1nxy; k22 += r1xsq;+ + // add the influnce from r2+ cpFloat b_i_inv = b->i_inv;+ cpFloat r2xsq = r2.x * r2.x * b_i_inv;+ cpFloat r2ysq = r2.y * r2.y * b_i_inv;+ cpFloat r2nxy = -r2.x * r2.y * b_i_inv;+ k11 += r2ysq; k12 += r2nxy;+ k21 += r2nxy; k22 += r2xsq;+ + // invert+ cpFloat det = k11*k22 - k12*k21;+ cpAssertSoft(det != 0.0, "Unsolvable constraint.");+ + cpFloat det_inv = 1.0f/det;+ return cpMat2x2New(+ k22*det_inv, -k12*det_inv,+ -k21*det_inv, k11*det_inv+ );+}++static inline cpFloat+bias_coef(cpFloat errorBias, cpFloat dt)+{+ return 1.0f - cpfpow(errorBias, dt);+}+++//MARK: Spaces++#define cpAssertSpaceUnlocked(space) \+ cpAssertHard(!space->locked, \+ "This operation cannot be done safely during a call to cpSpaceStep() or during a query. " \+ "Put these calls into a post-step callback." \+ );++void cpSpaceSetStaticBody(cpSpace *space, cpBody *body);++extern cpCollisionHandler cpCollisionHandlerDoNothing;++void cpSpaceProcessComponents(cpSpace *space, cpFloat dt);++void cpSpacePushFreshContactBuffer(cpSpace *space);+struct cpContact *cpContactBufferGetArray(cpSpace *space);+void cpSpacePushContacts(cpSpace *space, int count);++cpPostStepCallback *cpSpaceGetPostStepCallback(cpSpace *space, void *key);++cpBool cpSpaceArbiterSetFilter(cpArbiter *arb, cpSpace *space);+void cpSpaceFilterArbiters(cpSpace *space, cpBody *body, cpShape *filter);++void cpSpaceActivateBody(cpSpace *space, cpBody *body);+void cpSpaceLock(cpSpace *space);+void cpSpaceUnlock(cpSpace *space, cpBool runPostStep);++static inline void+cpSpaceUncacheArbiter(cpSpace *space, cpArbiter *arb)+{+ const cpShape *a = arb->a, *b = arb->b;+ const cpShape *shape_pair[] = {a, b};+ cpHashValue arbHashID = CP_HASH_PAIR((cpHashValue)a, (cpHashValue)b);+ cpHashSetRemove(space->cachedArbiters, arbHashID, shape_pair);+ cpArrayDeleteObj(space->arbiters, arb);+}++static inline cpArray *+cpSpaceArrayForBodyType(cpSpace *space, cpBodyType type)+{+ return (type == CP_BODY_TYPE_STATIC ? space->staticBodies : space->dynamicBodies);+}++void cpShapeUpdateFunc(cpShape *shape, void *unused);+cpCollisionID cpSpaceCollideShapes(cpShape *a, cpShape *b, cpCollisionID id, cpSpace *space);+++//MARK: Foreach loops++static inline cpConstraint *+cpConstraintNext(cpConstraint *node, cpBody *body)+{+ return (node->a == body ? node->next_a : node->next_b);+}++#define CP_BODY_FOREACH_CONSTRAINT(bdy, var)\+ for(cpConstraint *var = bdy->constraintList; var; var = cpConstraintNext(var, bdy))++static inline cpArbiter *+cpArbiterNext(cpArbiter *node, cpBody *body)+{+ return (node->body_a == body ? node->thread_a.next : node->thread_b.next);+}++#define CP_BODY_FOREACH_ARBITER(bdy, var)\+ for(cpArbiter *var = bdy->arbiterList; var; var = cpArbiterNext(var, bdy))++#define CP_BODY_FOREACH_SHAPE(body, var)\+ for(cpShape *var = body->shapeList; var; var = var->next)++#define CP_BODY_FOREACH_COMPONENT(root, var)\+ for(cpBody *var = root; var; var = var->sleeping.next)++#endif
+ Chipmunk2D-7.0.2/include/chipmunk/chipmunk_structs.h view
@@ -0,0 +1,450 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++// All of the struct definitions for Chipmunk should be considered part of the private API.+// However, it is very valuable to know the struct sizes for preallocating memory.++#ifndef CHIPMUNK_STRUCTS_H+#define CHIPMUNK_STRUCTS_H++#include "chipmunk/chipmunk.h"++struct cpArray {+ int num, max;+ void **arr;+};++struct cpBody {+ // Integration functions+ cpBodyVelocityFunc velocity_func;+ cpBodyPositionFunc position_func;+ + // mass and it's inverse+ cpFloat m;+ cpFloat m_inv;+ + // moment of inertia and it's inverse+ cpFloat i;+ cpFloat i_inv;+ + // center of gravity+ cpVect cog;+ + // position, velocity, force+ cpVect p;+ cpVect v;+ cpVect f;+ + // Angle, angular velocity, torque (radians)+ cpFloat a;+ cpFloat w;+ cpFloat t;+ + cpTransform transform;+ + cpDataPointer userData;+ + // "pseudo-velocities" used for eliminating overlap.+ // Erin Catto has some papers that talk about what these are.+ cpVect v_bias;+ cpFloat w_bias;+ + cpSpace *space;+ + cpShape *shapeList;+ cpArbiter *arbiterList;+ cpConstraint *constraintList;+ + struct {+ cpBody *root;+ cpBody *next;+ cpFloat idleTime;+ } sleeping;+};++enum cpArbiterState {+ // Arbiter is active and its the first collision.+ CP_ARBITER_STATE_FIRST_COLLISION,+ // Arbiter is active and its not the first collision.+ CP_ARBITER_STATE_NORMAL,+ // Collision has been explicitly ignored.+ // Either by returning false from a begin collision handler or calling cpArbiterIgnore().+ CP_ARBITER_STATE_IGNORE,+ // Collison is no longer active. A space will cache an arbiter for up to cpSpace.collisionPersistence more steps.+ CP_ARBITER_STATE_CACHED,+ // Collison arbiter is invalid because one of the shapes was removed.+ CP_ARBITER_STATE_INVALIDATED,+};++struct cpArbiterThread {+ struct cpArbiter *next, *prev;+};++struct cpContact {+ cpVect r1, r2;+ + cpFloat nMass, tMass;+ cpFloat bounce; // TODO: look for an alternate bounce solution.++ cpFloat jnAcc, jtAcc, jBias;+ cpFloat bias;+ + cpHashValue hash;+};++struct cpCollisionInfo {+ const cpShape *a, *b;+ cpCollisionID id;+ + cpVect n;+ + int count;+ // TODO Should this be a unique struct type?+ struct cpContact *arr;+};++struct cpArbiter {+ cpFloat e;+ cpFloat u;+ cpVect surface_vr;+ + cpDataPointer data;+ + const cpShape *a, *b;+ cpBody *body_a, *body_b;+ struct cpArbiterThread thread_a, thread_b;+ + int count;+ struct cpContact *contacts;+ cpVect n;+ + // Regular, wildcard A and wildcard B collision handlers.+ cpCollisionHandler *handler, *handlerA, *handlerB;+ cpBool swapped;+ + cpTimestamp stamp;+ enum cpArbiterState state;+};++struct cpShapeMassInfo {+ cpFloat m;+ cpFloat i;+ cpVect cog;+ cpFloat area;+};++typedef enum cpShapeType{+ CP_CIRCLE_SHAPE,+ CP_SEGMENT_SHAPE,+ CP_POLY_SHAPE,+ CP_NUM_SHAPES+} cpShapeType;++typedef cpBB (*cpShapeCacheDataImpl)(cpShape *shape, cpTransform transform);+typedef void (*cpShapeDestroyImpl)(cpShape *shape);+typedef void (*cpShapePointQueryImpl)(const cpShape *shape, cpVect p, cpPointQueryInfo *info);+typedef void (*cpShapeSegmentQueryImpl)(const cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info);++typedef struct cpShapeClass cpShapeClass;++struct cpShapeClass {+ cpShapeType type;+ + cpShapeCacheDataImpl cacheData;+ cpShapeDestroyImpl destroy;+ cpShapePointQueryImpl pointQuery;+ cpShapeSegmentQueryImpl segmentQuery;+};++struct cpShape {+ const cpShapeClass *klass;+ + cpSpace *space;+ cpBody *body;+ struct cpShapeMassInfo massInfo;+ cpBB bb;+ + cpBool sensor;+ + cpFloat e;+ cpFloat u;+ cpVect surfaceV;++ cpDataPointer userData;+ + cpCollisionType type;+ cpShapeFilter filter;+ + cpShape *next;+ cpShape *prev;+ + cpHashValue hashid;+};++struct cpCircleShape {+ cpShape shape;+ + cpVect c, tc;+ cpFloat r;+};++struct cpSegmentShape {+ cpShape shape;+ + cpVect a, b, n;+ cpVect ta, tb, tn;+ cpFloat r;+ + cpVect a_tangent, b_tangent;+};++struct cpSplittingPlane {+ cpVect v0, n;+};++#define CP_POLY_SHAPE_INLINE_ALLOC 6++struct cpPolyShape {+ cpShape shape;+ + cpFloat r;+ + int count;+ // The untransformed planes are appended at the end of the transformed planes.+ struct cpSplittingPlane *planes;+ + // Allocate a small number of splitting planes internally for simple poly.+ struct cpSplittingPlane _planes[2*CP_POLY_SHAPE_INLINE_ALLOC];+};++typedef void (*cpConstraintPreStepImpl)(cpConstraint *constraint, cpFloat dt);+typedef void (*cpConstraintApplyCachedImpulseImpl)(cpConstraint *constraint, cpFloat dt_coef);+typedef void (*cpConstraintApplyImpulseImpl)(cpConstraint *constraint, cpFloat dt);+typedef cpFloat (*cpConstraintGetImpulseImpl)(cpConstraint *constraint);++typedef struct cpConstraintClass {+ cpConstraintPreStepImpl preStep;+ cpConstraintApplyCachedImpulseImpl applyCachedImpulse;+ cpConstraintApplyImpulseImpl applyImpulse;+ cpConstraintGetImpulseImpl getImpulse;+} cpConstraintClass;++struct cpConstraint {+ const cpConstraintClass *klass;+ + cpSpace *space;+ + cpBody *a, *b;+ cpConstraint *next_a, *next_b;+ + cpFloat maxForce;+ cpFloat errorBias;+ cpFloat maxBias;+ + cpBool collideBodies;+ + cpConstraintPreSolveFunc preSolve;+ cpConstraintPostSolveFunc postSolve;+ + cpDataPointer userData;+};++struct cpPinJoint {+ cpConstraint constraint;+ cpVect anchorA, anchorB;+ cpFloat dist;+ + cpVect r1, r2;+ cpVect n;+ cpFloat nMass;+ + cpFloat jnAcc;+ cpFloat bias;+};++struct cpSlideJoint {+ cpConstraint constraint;+ cpVect anchorA, anchorB;+ cpFloat min, max;+ + cpVect r1, r2;+ cpVect n;+ cpFloat nMass;+ + cpFloat jnAcc;+ cpFloat bias;+};++struct cpPivotJoint {+ cpConstraint constraint;+ cpVect anchorA, anchorB;+ + cpVect r1, r2;+ cpMat2x2 k;+ + cpVect jAcc;+ cpVect bias;+};++struct cpGrooveJoint {+ cpConstraint constraint;+ cpVect grv_n, grv_a, grv_b;+ cpVect anchorB;+ + cpVect grv_tn;+ cpFloat clamp;+ cpVect r1, r2;+ cpMat2x2 k;+ + cpVect jAcc;+ cpVect bias;+};++struct cpDampedSpring {+ cpConstraint constraint;+ cpVect anchorA, anchorB;+ cpFloat restLength;+ cpFloat stiffness;+ cpFloat damping;+ cpDampedSpringForceFunc springForceFunc;+ + cpFloat target_vrn;+ cpFloat v_coef;+ + cpVect r1, r2;+ cpFloat nMass;+ cpVect n;+ + cpFloat jAcc;+};++struct cpDampedRotarySpring {+ cpConstraint constraint;+ cpFloat restAngle;+ cpFloat stiffness;+ cpFloat damping;+ cpDampedRotarySpringTorqueFunc springTorqueFunc;+ + cpFloat target_wrn;+ cpFloat w_coef;+ + cpFloat iSum;+ cpFloat jAcc;+};++struct cpRotaryLimitJoint {+ cpConstraint constraint;+ cpFloat min, max;+ + cpFloat iSum;+ + cpFloat bias;+ cpFloat jAcc;+};++struct cpRatchetJoint {+ cpConstraint constraint;+ cpFloat angle, phase, ratchet;+ + cpFloat iSum;+ + cpFloat bias;+ cpFloat jAcc;+};++struct cpGearJoint {+ cpConstraint constraint;+ cpFloat phase, ratio;+ cpFloat ratio_inv;+ + cpFloat iSum;+ + cpFloat bias;+ cpFloat jAcc;+};++struct cpSimpleMotor {+ cpConstraint constraint;+ cpFloat rate;+ + cpFloat iSum;+ + cpFloat jAcc;+};++typedef struct cpContactBufferHeader cpContactBufferHeader;+typedef void (*cpSpaceArbiterApplyImpulseFunc)(cpArbiter *arb);++struct cpSpace {+ int iterations;+ + cpVect gravity;+ cpFloat damping;+ + cpFloat idleSpeedThreshold;+ cpFloat sleepTimeThreshold;+ + cpFloat collisionSlop;+ cpFloat collisionBias;+ cpTimestamp collisionPersistence;+ + cpDataPointer userData;+ + cpTimestamp stamp;+ cpFloat curr_dt;++ cpArray *dynamicBodies;+ cpArray *staticBodies;+ cpArray *rousedBodies;+ cpArray *sleepingComponents;+ + cpHashValue shapeIDCounter;+ cpSpatialIndex *staticShapes;+ cpSpatialIndex *dynamicShapes;+ + cpArray *constraints;+ + cpArray *arbiters;+ cpContactBufferHeader *contactBuffersHead;+ cpHashSet *cachedArbiters;+ cpArray *pooledArbiters;+ + cpArray *allocatedBuffers;+ unsigned int locked;+ + cpBool usesWildcards;+ cpHashSet *collisionHandlers;+ cpCollisionHandler defaultHandler;+ + cpBool skipPostStep;+ cpArray *postStepCallbacks;+ + cpBody *staticBody;+ cpBody _staticBody;+};++typedef struct cpPostStepCallback {+ cpPostStepFunc func;+ void *key;+ void *data;+} cpPostStepCallback;++#endif
+ Chipmunk2D-7.0.2/include/chipmunk/chipmunk_types.h view
@@ -0,0 +1,268 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#ifndef CHIPMUNK_TYPES_H+#define CHIPMUNK_TYPES_H++#include <stdint.h>+#include <float.h>+#include <math.h>++#ifdef __APPLE__+ #include "TargetConditionals.h"+#endif++// Use CGTypes by default on iOS and Mac.+// Also enables usage of doubles on 64 bit.+// Performance is usually very comparable when the CPU cache is well utilised.+#if (TARGET_OS_IPHONE || TARGET_OS_MAC) && (!defined CP_USE_CGTYPES)+ #define CP_USE_CGTYPES 1+#endif++#if CP_USE_CGTYPES+ #if TARGET_OS_IPHONE+ #include <CoreGraphics/CGGeometry.h>+ #include <CoreGraphics/CGAffineTransform.h>+ #elif TARGET_OS_MAC+ #include <ApplicationServices/ApplicationServices.h>+ #endif+ + #if defined(__LP64__) && __LP64__+ #define CP_USE_DOUBLES 1+ #else+ #define CP_USE_DOUBLES 0+ #endif+#endif++#ifndef CP_USE_DOUBLES+ // Use doubles by default for higher precision.+ #define CP_USE_DOUBLES 1+#endif++/// @defgroup basicTypes Basic Types+/// Most of these types can be configured at compile time.+/// @{++#if CP_USE_DOUBLES+/// Chipmunk's floating point type.+/// Can be reconfigured at compile time.+ typedef double cpFloat;+ #define cpfsqrt sqrt+ #define cpfsin sin+ #define cpfcos cos+ #define cpfacos acos+ #define cpfatan2 atan2+ #define cpfmod fmod+ #define cpfexp exp+ #define cpfpow pow+ #define cpffloor floor+ #define cpfceil ceil+ #define CPFLOAT_MIN DBL_MIN+#else+ typedef float cpFloat;+ #define cpfsqrt sqrtf+ #define cpfsin sinf+ #define cpfcos cosf+ #define cpfacos acosf+ #define cpfatan2 atan2f+ #define cpfmod fmodf+ #define cpfexp expf+ #define cpfpow powf+ #define cpffloor floorf+ #define cpfceil ceilf+ #define CPFLOAT_MIN FLT_MIN+#endif++#ifndef INFINITY+ #ifdef _MSC_VER+ union MSVC_EVIL_FLOAT_HACK+ {+ unsigned __int8 Bytes[4];+ float Value;+ };+ static union MSVC_EVIL_FLOAT_HACK INFINITY_HACK = {{0x00, 0x00, 0x80, 0x7F}};+ #define INFINITY (INFINITY_HACK.Value)+ #endif+ + #ifdef __GNUC__+ #define INFINITY (__builtin_inf())+ #endif+ + #ifndef INFINITY+ #define INFINITY (1e1000)+ #endif+#endif+++#define CP_PI ((cpFloat)3.14159265358979323846264338327950288)+++/// Return the max of two cpFloats.+static inline cpFloat cpfmax(cpFloat a, cpFloat b)+{+ return (a > b) ? a : b;+}++/// Return the min of two cpFloats.+static inline cpFloat cpfmin(cpFloat a, cpFloat b)+{+ return (a < b) ? a : b;+}++/// Return the absolute value of a cpFloat.+static inline cpFloat cpfabs(cpFloat f)+{+ return (f < 0) ? -f : f;+}++/// Clamp @c f to be between @c min and @c max.+static inline cpFloat cpfclamp(cpFloat f, cpFloat min, cpFloat max)+{+ return cpfmin(cpfmax(f, min), max);+}++/// Clamp @c f to be between 0 and 1.+static inline cpFloat cpfclamp01(cpFloat f)+{+ return cpfmax(0.0f, cpfmin(f, 1.0f));+}++++/// Linearly interpolate (or extrapolate) between @c f1 and @c f2 by @c t percent.+static inline cpFloat cpflerp(cpFloat f1, cpFloat f2, cpFloat t)+{+ return f1*(1.0f - t) + f2*t;+}++/// Linearly interpolate from @c f1 to @c f2 by no more than @c d.+static inline cpFloat cpflerpconst(cpFloat f1, cpFloat f2, cpFloat d)+{+ return f1 + cpfclamp(f2 - f1, -d, d);+}++/// Hash value type.+#ifdef CP_HASH_VALUE_TYPE+ typedef CP_HASH_VALUE_TYPE cpHashValue;+#else+ typedef uintptr_t cpHashValue;+#endif++/// Type used internally to cache colliding object info for cpCollideShapes().+/// Should be at least 32 bits.+typedef uint32_t cpCollisionID;++// Oh C, how we love to define our own boolean types to get compiler compatibility+/// Chipmunk's boolean type.+#ifdef CP_BOOL_TYPE+ typedef CP_BOOL_TYPE cpBool;+#else+ typedef unsigned char cpBool;+#endif++#ifndef cpTrue+/// true value.+ #define cpTrue 1+#endif++#ifndef cpFalse+/// false value.+ #define cpFalse 0+#endif++#ifdef CP_DATA_POINTER_TYPE+ typedef CP_DATA_POINTER_TYPE cpDataPointer;+#else+/// Type used for user data pointers.+ typedef void * cpDataPointer;+#endif++#ifdef CP_COLLISION_TYPE_TYPE+ typedef CP_COLLISION_TYPE_TYPE cpCollisionType;+#else+/// Type used for cpSpace.collision_type.+ typedef uintptr_t cpCollisionType;+#endif++#ifdef CP_GROUP_TYPE+ typedef CP_GROUP_TYPE cpGroup;+#else+/// Type used for cpShape.group.+ typedef uintptr_t cpGroup;+#endif++#ifdef CP_BITMASK_TYPE+ typedef CP_BITMASK_TYPE cpBitmask;+#else+/// Type used for cpShapeFilter category and mask.+ typedef unsigned int cpBitmask;+#endif++#ifdef CP_TIMESTAMP_TYPE+ typedef CP_TIMESTAMP_TYPE cpTimestamp;+#else+/// Type used for various timestamps in Chipmunk.+ typedef unsigned int cpTimestamp;+#endif++#ifndef CP_NO_GROUP+/// Value for cpShape.group signifying that a shape is in no group.+ #define CP_NO_GROUP ((cpGroup)0)+#endif++#ifndef CP_ALL_CATEGORIES+/// Value for cpShape.layers signifying that a shape is in every layer.+ #define CP_ALL_CATEGORIES (~(cpBitmask)0)+#endif++#ifndef CP_WILDCARD_COLLISION_TYPE+/// cpCollisionType value internally reserved for hashing wildcard handlers.+ #define CP_WILDCARD_COLLISION_TYPE (~(cpCollisionType)0)+#endif++/// @}++// CGPoints are structurally the same, and allow+// easy interoperability with other Cocoa libraries+#if CP_USE_CGTYPES+ typedef CGPoint cpVect;+#else+/// Chipmunk's 2D vector type.+/// @addtogroup cpVect+ typedef struct cpVect{cpFloat x,y;} cpVect;+#endif++#if CP_USE_CGTYPES+ typedef CGAffineTransform cpTransform;+#else+ /// Column major affine transform.+ typedef struct cpTransform {+ cpFloat a, b, c, d, tx, ty;+ } cpTransform;+#endif++// NUKE+typedef struct cpMat2x2 {+ // Row major [[a, b][c d]]+ cpFloat a, b, c, d;+} cpMat2x2;++#endif
+ Chipmunk2D-7.0.2/include/chipmunk/chipmunk_unsafe.h view
@@ -0,0 +1,66 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/* This header defines a number of "unsafe" operations on Chipmunk objects.+ * In this case "unsafe" is referring to operations which may reduce the+ * physical accuracy or numerical stability of the simulation, but will not+ * cause crashes.+ *+ * The prime example is mutating collision shapes. Chipmunk does not support+ * this directly. Mutating shapes using this API will caused objects in contact+ * to be pushed apart using Chipmunk's overlap solver, but not using real+ * persistent velocities. Probably not what you meant, but perhaps close enough.+ */++/// @defgroup unsafe Chipmunk Unsafe Shape Operations+/// These functions are used for mutating collision shapes.+/// Chipmunk does not have any way to get velocity information on changing shapes,+/// so the results will be unrealistic. You must explicity include the chipmunk_unsafe.h header to use them.+/// @{++#ifndef CHIPMUNK_UNSAFE_H+#define CHIPMUNK_UNSAFE_H++#ifdef __cplusplus+extern "C" {+#endif++/// Set the radius of a circle shape.+CP_EXPORT void cpCircleShapeSetRadius(cpShape *shape, cpFloat radius);+/// Set the offset of a circle shape.+CP_EXPORT void cpCircleShapeSetOffset(cpShape *shape, cpVect offset);++/// Set the endpoints of a segment shape.+CP_EXPORT void cpSegmentShapeSetEndpoints(cpShape *shape, cpVect a, cpVect b);+/// Set the radius of a segment shape.+CP_EXPORT void cpSegmentShapeSetRadius(cpShape *shape, cpFloat radius);++/// Set the vertexes of a poly shape.+CP_EXPORT void cpPolyShapeSetVerts(cpShape *shape, int count, cpVect *verts, cpTransform transform);+CP_EXPORT void cpPolyShapeSetVertsRaw(cpShape *shape, int count, cpVect *verts);+/// Set the radius of a poly shape.+CP_EXPORT void cpPolyShapeSetRadius(cpShape *shape, cpFloat radius);++#ifdef __cplusplus+}+#endif+#endif+/// @}
+ Chipmunk2D-7.0.2/include/chipmunk/cpArbiter.h view
@@ -0,0 +1,145 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpArbiter cpArbiter+/// The cpArbiter struct tracks pairs of colliding shapes.+/// They are also used in conjuction with collision handler callbacks+/// allowing you to retrieve information on the collision or change it.+/// A unique arbiter value is used for each pair of colliding objects. It persists until the shapes separate.+/// @{++#define CP_MAX_CONTACTS_PER_ARBITER 2++/// Get the restitution (elasticity) that will be applied to the pair of colliding objects.+CP_EXPORT cpFloat cpArbiterGetRestitution(const cpArbiter *arb);+/// Override the restitution (elasticity) that will be applied to the pair of colliding objects.+CP_EXPORT void cpArbiterSetRestitution(cpArbiter *arb, cpFloat restitution);+/// Get the friction coefficient that will be applied to the pair of colliding objects.+CP_EXPORT cpFloat cpArbiterGetFriction(const cpArbiter *arb);+/// Override the friction coefficient that will be applied to the pair of colliding objects.+CP_EXPORT void cpArbiterSetFriction(cpArbiter *arb, cpFloat friction);++// Get the relative surface velocity of the two shapes in contact.+CP_EXPORT cpVect cpArbiterGetSurfaceVelocity(cpArbiter *arb);++// Override the relative surface velocity of the two shapes in contact.+// By default this is calculated to be the difference of the two surface velocities clamped to the tangent plane.+CP_EXPORT void cpArbiterSetSurfaceVelocity(cpArbiter *arb, cpVect vr);++/// Get the user data pointer associated with this pair of colliding objects.+CP_EXPORT cpDataPointer cpArbiterGetUserData(const cpArbiter *arb);+/// Set a user data point associated with this pair of colliding objects.+/// If you need to perform any cleanup for this pointer, you must do it yourself, in the separate callback for instance.+CP_EXPORT void cpArbiterSetUserData(cpArbiter *arb, cpDataPointer userData);++/// Calculate the total impulse including the friction that was applied by this arbiter.+/// This function should only be called from a post-solve, post-step or cpBodyEachArbiter callback.+CP_EXPORT cpVect cpArbiterTotalImpulse(const cpArbiter *arb);+/// Calculate the amount of energy lost in a collision including static, but not dynamic friction.+/// This function should only be called from a post-solve, post-step or cpBodyEachArbiter callback.+CP_EXPORT cpFloat cpArbiterTotalKE(const cpArbiter *arb);++/// Mark a collision pair to be ignored until the two objects separate.+/// Pre-solve and post-solve callbacks will not be called, but the separate callback will be called.+CP_EXPORT cpBool cpArbiterIgnore(cpArbiter *arb);++/// Return the colliding shapes involved for this arbiter.+/// The order of their cpSpace.collision_type values will match+/// the order set when the collision handler was registered.+CP_EXPORT void cpArbiterGetShapes(const cpArbiter *arb, cpShape **a, cpShape **b);++/// A macro shortcut for defining and retrieving the shapes from an arbiter.+#define CP_ARBITER_GET_SHAPES(__arb__, __a__, __b__) cpShape *__a__, *__b__; cpArbiterGetShapes(__arb__, &__a__, &__b__);++/// Return the colliding bodies involved for this arbiter.+/// The order of the cpSpace.collision_type the bodies are associated with values will match+/// the order set when the collision handler was registered.+CP_EXPORT void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody **b);++/// A macro shortcut for defining and retrieving the bodies from an arbiter.+#define CP_ARBITER_GET_BODIES(__arb__, __a__, __b__) cpBody *__a__, *__b__; cpArbiterGetBodies(__arb__, &__a__, &__b__);++/// A struct that wraps up the important collision data for an arbiter.+struct cpContactPointSet {+ /// The number of contact points in the set.+ int count;+ + /// The normal of the collision.+ cpVect normal;+ + /// The array of contact points.+ struct {+ /// The position of the contact on the surface of each shape.+ cpVect pointA, pointB;+ /// Penetration distance of the two shapes. Overlapping means it will be negative.+ /// This value is calculated as cpvdot(cpvsub(point2, point1), normal) and is ignored by cpArbiterSetContactPointSet().+ cpFloat distance;+ } points[CP_MAX_CONTACTS_PER_ARBITER];+};++/// Return a contact set from an arbiter.+CP_EXPORT cpContactPointSet cpArbiterGetContactPointSet(const cpArbiter *arb);++/// Replace the contact point set for an arbiter.+/// This can be a very powerful feature, but use it with caution!+CP_EXPORT void cpArbiterSetContactPointSet(cpArbiter *arb, cpContactPointSet *set);++/// Returns true if this is the first step a pair of objects started colliding.+CP_EXPORT cpBool cpArbiterIsFirstContact(const cpArbiter *arb);+/// Returns true if the separate callback is due to a shape being removed from the space.+CP_EXPORT cpBool cpArbiterIsRemoval(const cpArbiter *arb);++/// Get the number of contact points for this arbiter.+CP_EXPORT int cpArbiterGetCount(const cpArbiter *arb);+/// Get the normal of the collision.+CP_EXPORT cpVect cpArbiterGetNormal(const cpArbiter *arb);+/// Get the position of the @c ith contact point on the surface of the first shape.+CP_EXPORT cpVect cpArbiterGetPointA(const cpArbiter *arb, int i);+/// Get the position of the @c ith contact point on the surface of the second shape.+CP_EXPORT cpVect cpArbiterGetPointB(const cpArbiter *arb, int i);+/// Get the depth of the @c ith contact point.+CP_EXPORT cpFloat cpArbiterGetDepth(const cpArbiter *arb, int i);++/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly.+/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own.+CP_EXPORT cpBool cpArbiterCallWildcardBeginA(cpArbiter *arb, cpSpace *space);+/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly.+/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own.+CP_EXPORT cpBool cpArbiterCallWildcardBeginB(cpArbiter *arb, cpSpace *space);++/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly.+/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own.+CP_EXPORT cpBool cpArbiterCallWildcardPreSolveA(cpArbiter *arb, cpSpace *space);+/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly.+/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own.+CP_EXPORT cpBool cpArbiterCallWildcardPreSolveB(cpArbiter *arb, cpSpace *space);++/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly.+CP_EXPORT void cpArbiterCallWildcardPostSolveA(cpArbiter *arb, cpSpace *space);+/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly.+CP_EXPORT void cpArbiterCallWildcardPostSolveB(cpArbiter *arb, cpSpace *space);++/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly.+CP_EXPORT void cpArbiterCallWildcardSeparateA(cpArbiter *arb, cpSpace *space);+/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly.+CP_EXPORT void cpArbiterCallWildcardSeparateB(cpArbiter *arb, cpSpace *space);++/// @}
+ Chipmunk2D-7.0.2/include/chipmunk/cpBB.h view
@@ -0,0 +1,187 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#ifndef CHIPMUNK_BB_H+#define CHIPMUNK_BB_H++#include "chipmunk_types.h"+#include "cpVect.h"++/// @defgroup cpBBB cpBB+/// Chipmunk's axis-aligned 2D bounding box type along with a few handy routines.+/// @{++/// Chipmunk's axis-aligned 2D bounding box type. (left, bottom, right, top)+typedef struct cpBB{+ cpFloat l, b, r ,t;+} cpBB;++/// Convenience constructor for cpBB structs.+static inline cpBB cpBBNew(const cpFloat l, const cpFloat b, const cpFloat r, const cpFloat t)+{+ cpBB bb = {l, b, r, t};+ return bb;+}++/// Constructs a cpBB centered on a point with the given extents (half sizes).+static inline cpBB+cpBBNewForExtents(const cpVect c, const cpFloat hw, const cpFloat hh)+{+ return cpBBNew(c.x - hw, c.y - hh, c.x + hw, c.y + hh);+}++/// Constructs a cpBB for a circle with the given position and radius.+static inline cpBB cpBBNewForCircle(const cpVect p, const cpFloat r)+{+ return cpBBNewForExtents(p, r, r);+}++/// Returns true if @c a and @c b intersect.+static inline cpBool cpBBIntersects(const cpBB a, const cpBB b)+{+ return (a.l <= b.r && b.l <= a.r && a.b <= b.t && b.b <= a.t);+}++/// Returns true if @c other lies completely within @c bb.+static inline cpBool cpBBContainsBB(const cpBB bb, const cpBB other)+{+ return (bb.l <= other.l && bb.r >= other.r && bb.b <= other.b && bb.t >= other.t);+}++/// Returns true if @c bb contains @c v.+static inline cpBool cpBBContainsVect(const cpBB bb, const cpVect v)+{+ return (bb.l <= v.x && bb.r >= v.x && bb.b <= v.y && bb.t >= v.y);+}++/// Returns a bounding box that holds both bounding boxes.+static inline cpBB cpBBMerge(const cpBB a, const cpBB b){+ return cpBBNew(+ cpfmin(a.l, b.l),+ cpfmin(a.b, b.b),+ cpfmax(a.r, b.r),+ cpfmax(a.t, b.t)+ );+}++/// Returns a bounding box that holds both @c bb and @c v.+static inline cpBB cpBBExpand(const cpBB bb, const cpVect v){+ return cpBBNew(+ cpfmin(bb.l, v.x),+ cpfmin(bb.b, v.y),+ cpfmax(bb.r, v.x),+ cpfmax(bb.t, v.y)+ );+}++/// Returns the center of a bounding box.+static inline cpVect+cpBBCenter(cpBB bb)+{+ return cpvlerp(cpv(bb.l, bb.b), cpv(bb.r, bb.t), 0.5f);+}++/// Returns the area of the bounding box.+static inline cpFloat cpBBArea(cpBB bb)+{+ return (bb.r - bb.l)*(bb.t - bb.b);+}++/// Merges @c a and @c b and returns the area of the merged bounding box.+static inline cpFloat cpBBMergedArea(cpBB a, cpBB b)+{+ return (cpfmax(a.r, b.r) - cpfmin(a.l, b.l))*(cpfmax(a.t, b.t) - cpfmin(a.b, b.b));+}++/// Returns the fraction along the segment query the cpBB is hit. Returns INFINITY if it doesn't hit.+static inline cpFloat cpBBSegmentQuery(cpBB bb, cpVect a, cpVect b)+{+ cpVect delta = cpvsub(b, a);+ cpFloat tmin = -INFINITY, tmax = INFINITY;+ + if(delta.x == 0.0f){+ if(a.x < bb.l || bb.r < a.x) return INFINITY;+ } else {+ cpFloat t1 = (bb.l - a.x)/delta.x;+ cpFloat t2 = (bb.r - a.x)/delta.x;+ tmin = cpfmax(tmin, cpfmin(t1, t2));+ tmax = cpfmin(tmax, cpfmax(t1, t2));+ }+ + if(delta.y == 0.0f){+ if(a.y < bb.b || bb.t < a.y) return INFINITY;+ } else {+ cpFloat t1 = (bb.b - a.y)/delta.y;+ cpFloat t2 = (bb.t - a.y)/delta.y;+ tmin = cpfmax(tmin, cpfmin(t1, t2));+ tmax = cpfmin(tmax, cpfmax(t1, t2));+ }+ + if(tmin <= tmax && 0.0f <= tmax && tmin <= 1.0f){+ return cpfmax(tmin, 0.0f);+ } else {+ return INFINITY;+ }+}++/// Return true if the bounding box intersects the line segment with ends @c a and @c b.+static inline cpBool cpBBIntersectsSegment(cpBB bb, cpVect a, cpVect b)+{+ return (cpBBSegmentQuery(bb, a, b) != INFINITY);+}++/// Clamp a vector to a bounding box.+static inline cpVect+cpBBClampVect(const cpBB bb, const cpVect v)+{+ return cpv(cpfclamp(v.x, bb.l, bb.r), cpfclamp(v.y, bb.b, bb.t));+}++/// Wrap a vector to a bounding box.+static inline cpVect+cpBBWrapVect(const cpBB bb, const cpVect v)+{+ cpFloat dx = cpfabs(bb.r - bb.l);+ cpFloat modx = cpfmod(v.x - bb.l, dx);+ cpFloat x = (modx > 0.0f) ? modx : modx + dx;+ + cpFloat dy = cpfabs(bb.t - bb.b);+ cpFloat mody = cpfmod(v.y - bb.b, dy);+ cpFloat y = (mody > 0.0f) ? mody : mody + dy;+ + return cpv(x + bb.l, y + bb.b);+}++/// Returns a bounding box offseted by @c v.+static inline cpBB+cpBBOffset(const cpBB bb, const cpVect v)+{+ return cpBBNew(+ bb.l + v.x,+ bb.b + v.y,+ bb.r + v.x,+ bb.t + v.y+ );+}++///@}++#endif
+ Chipmunk2D-7.0.2/include/chipmunk/cpBody.h view
@@ -0,0 +1,189 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpBody cpBody+/// Chipmunk's rigid body type. Rigid bodies hold the physical properties of an object like+/// it's mass, and position and velocity of it's center of gravity. They don't have an shape on their own.+/// They are given a shape by creating collision shapes (cpShape) that point to the body.+/// @{++typedef enum cpBodyType {+ /// A dynamic body is one that is affected by gravity, forces, and collisions.+ /// This is the default body type.+ CP_BODY_TYPE_DYNAMIC,+ /// A kinematic body is an infinite mass, user controlled body that is not affected by gravity, forces or collisions.+ /// Instead the body only moves based on it's velocity.+ /// Dynamic bodies collide normally with kinematic bodies, though the kinematic body will be unaffected.+ /// Collisions between two kinematic bodies, or a kinematic body and a static body produce collision callbacks, but no collision response.+ CP_BODY_TYPE_KINEMATIC,+ /// A static body is a body that never (or rarely) moves. If you move a static body, you must call one of the cpSpaceReindex*() functions.+ /// Chipmunk uses this information to optimize the collision detection.+ /// Static bodies do not produce collision callbacks when colliding with other static bodies.+ CP_BODY_TYPE_STATIC,+} cpBodyType;++/// Rigid body velocity update function type.+typedef void (*cpBodyVelocityFunc)(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt);+/// Rigid body position update function type.+typedef void (*cpBodyPositionFunc)(cpBody *body, cpFloat dt);++/// Allocate a cpBody.+CP_EXPORT cpBody* cpBodyAlloc(void);+/// Initialize a cpBody.+CP_EXPORT cpBody* cpBodyInit(cpBody *body, cpFloat mass, cpFloat moment);+/// Allocate and initialize a cpBody.+CP_EXPORT cpBody* cpBodyNew(cpFloat mass, cpFloat moment);++/// Allocate and initialize a cpBody, and set it as a kinematic body.+CP_EXPORT cpBody* cpBodyNewKinematic(void);+/// Allocate and initialize a cpBody, and set it as a static body.+CP_EXPORT cpBody* cpBodyNewStatic(void);++/// Destroy a cpBody.+CP_EXPORT void cpBodyDestroy(cpBody *body);+/// Destroy and free a cpBody.+CP_EXPORT void cpBodyFree(cpBody *body);++// Defined in cpSpace.c+/// Wake up a sleeping or idle body.+CP_EXPORT void cpBodyActivate(cpBody *body);+/// Wake up any sleeping or idle bodies touching a static body.+CP_EXPORT void cpBodyActivateStatic(cpBody *body, cpShape *filter);++/// Force a body to fall asleep immediately.+CP_EXPORT void cpBodySleep(cpBody *body);+/// Force a body to fall asleep immediately along with other bodies in a group.+CP_EXPORT void cpBodySleepWithGroup(cpBody *body, cpBody *group);++/// Returns true if the body is sleeping.+CP_EXPORT cpBool cpBodyIsSleeping(const cpBody *body);++/// Get the type of the body.+CP_EXPORT cpBodyType cpBodyGetType(cpBody *body);+/// Set the type of the body.+CP_EXPORT void cpBodySetType(cpBody *body, cpBodyType type);++/// Get the space this body is added to.+CP_EXPORT cpSpace* cpBodyGetSpace(const cpBody *body);++/// Get the mass of the body.+CP_EXPORT cpFloat cpBodyGetMass(const cpBody *body);+/// Set the mass of the body.+CP_EXPORT void cpBodySetMass(cpBody *body, cpFloat m);++/// Get the moment of inertia of the body.+CP_EXPORT cpFloat cpBodyGetMoment(const cpBody *body);+/// Set the moment of inertia of the body.+CP_EXPORT void cpBodySetMoment(cpBody *body, cpFloat i);++/// Set the position of a body.+CP_EXPORT cpVect cpBodyGetPosition(const cpBody *body);+/// Set the position of the body.+CP_EXPORT void cpBodySetPosition(cpBody *body, cpVect pos);++/// Get the offset of the center of gravity in body local coordinates.+CP_EXPORT cpVect cpBodyGetCenterOfGravity(const cpBody *body);+/// Set the offset of the center of gravity in body local coordinates.+CP_EXPORT void cpBodySetCenterOfGravity(cpBody *body, cpVect cog);++/// Get the velocity of the body.+CP_EXPORT cpVect cpBodyGetVelocity(const cpBody *body);+/// Set the velocity of the body.+CP_EXPORT void cpBodySetVelocity(cpBody *body, cpVect velocity);++/// Get the force applied to the body for the next time step.+CP_EXPORT cpVect cpBodyGetForce(const cpBody *body);+/// Set the force applied to the body for the next time step.+CP_EXPORT void cpBodySetForce(cpBody *body, cpVect force);++/// Get the angle of the body.+CP_EXPORT cpFloat cpBodyGetAngle(const cpBody *body);+/// Set the angle of a body.+CP_EXPORT void cpBodySetAngle(cpBody *body, cpFloat a);++/// Get the angular velocity of the body.+CP_EXPORT cpFloat cpBodyGetAngularVelocity(const cpBody *body);+/// Set the angular velocity of the body.+CP_EXPORT void cpBodySetAngularVelocity(cpBody *body, cpFloat angularVelocity);++/// Get the torque applied to the body for the next time step.+CP_EXPORT cpFloat cpBodyGetTorque(const cpBody *body);+/// Set the torque applied to the body for the next time step.+CP_EXPORT void cpBodySetTorque(cpBody *body, cpFloat torque);++/// Get the rotation vector of the body. (The x basis vector of it's transform.)+CP_EXPORT cpVect cpBodyGetRotation(const cpBody *body);++/// Get the user data pointer assigned to the body.+CP_EXPORT cpDataPointer cpBodyGetUserData(const cpBody *body);+/// Set the user data pointer assigned to the body.+CP_EXPORT void cpBodySetUserData(cpBody *body, cpDataPointer userData);++/// Set the callback used to update a body's velocity.+CP_EXPORT void cpBodySetVelocityUpdateFunc(cpBody *body, cpBodyVelocityFunc velocityFunc);+/// Set the callback used to update a body's position.+/// NOTE: It's not generally recommended to override this unless you call the default position update function.+CP_EXPORT void cpBodySetPositionUpdateFunc(cpBody *body, cpBodyPositionFunc positionFunc);++/// Default velocity integration function..+CP_EXPORT void cpBodyUpdateVelocity(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt);+/// Default position integration function.+CP_EXPORT void cpBodyUpdatePosition(cpBody *body, cpFloat dt);++/// Convert body relative/local coordinates to absolute/world coordinates.+CP_EXPORT cpVect cpBodyLocalToWorld(const cpBody *body, const cpVect point);+/// Convert body absolute/world coordinates to relative/local coordinates.+CP_EXPORT cpVect cpBodyWorldToLocal(const cpBody *body, const cpVect point);++/// Apply a force to a body. Both the force and point are expressed in world coordinates.+CP_EXPORT void cpBodyApplyForceAtWorldPoint(cpBody *body, cpVect force, cpVect point);+/// Apply a force to a body. Both the force and point are expressed in body local coordinates.+CP_EXPORT void cpBodyApplyForceAtLocalPoint(cpBody *body, cpVect force, cpVect point);++/// Apply an impulse to a body. Both the impulse and point are expressed in world coordinates.+CP_EXPORT void cpBodyApplyImpulseAtWorldPoint(cpBody *body, cpVect impulse, cpVect point);+/// Apply an impulse to a body. Both the impulse and point are expressed in body local coordinates.+CP_EXPORT void cpBodyApplyImpulseAtLocalPoint(cpBody *body, cpVect impulse, cpVect point);++/// Get the velocity on a body (in world units) at a point on the body in world coordinates.+CP_EXPORT cpVect cpBodyGetVelocityAtWorldPoint(const cpBody *body, cpVect point);+/// Get the velocity on a body (in world units) at a point on the body in local coordinates.+CP_EXPORT cpVect cpBodyGetVelocityAtLocalPoint(const cpBody *body, cpVect point);++/// Get the amount of kinetic energy contained by the body.+CP_EXPORT cpFloat cpBodyKineticEnergy(const cpBody *body);++/// Body/shape iterator callback function type. +typedef void (*cpBodyShapeIteratorFunc)(cpBody *body, cpShape *shape, void *data);+/// Call @c func once for each shape attached to @c body and added to the space.+CP_EXPORT void cpBodyEachShape(cpBody *body, cpBodyShapeIteratorFunc func, void *data);++/// Body/constraint iterator callback function type. +typedef void (*cpBodyConstraintIteratorFunc)(cpBody *body, cpConstraint *constraint, void *data);+/// Call @c func once for each constraint attached to @c body and added to the space.+CP_EXPORT void cpBodyEachConstraint(cpBody *body, cpBodyConstraintIteratorFunc func, void *data);++/// Body/arbiter iterator callback function type. +typedef void (*cpBodyArbiterIteratorFunc)(cpBody *body, cpArbiter *arbiter, void *data);+/// Call @c func once for each arbiter that is currently active on the body.+CP_EXPORT void cpBodyEachArbiter(cpBody *body, cpBodyArbiterIteratorFunc func, void *data);++///@}
+ Chipmunk2D-7.0.2/include/chipmunk/cpConstraint.h view
@@ -0,0 +1,95 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpConstraint cpConstraint+/// @{++/// Callback function type that gets called before solving a joint.+typedef void (*cpConstraintPreSolveFunc)(cpConstraint *constraint, cpSpace *space);+/// Callback function type that gets called after solving a joint.+typedef void (*cpConstraintPostSolveFunc)(cpConstraint *constraint, cpSpace *space);++/// Destroy a constraint.+CP_EXPORT void cpConstraintDestroy(cpConstraint *constraint);+/// Destroy and free a constraint.+CP_EXPORT void cpConstraintFree(cpConstraint *constraint);++/// Get the cpSpace this constraint is added to.+CP_EXPORT cpSpace* cpConstraintGetSpace(const cpConstraint *constraint);++/// Get the first body the constraint is attached to.+CP_EXPORT cpBody* cpConstraintGetBodyA(const cpConstraint *constraint);++/// Get the second body the constraint is attached to.+CP_EXPORT cpBody* cpConstraintGetBodyB(const cpConstraint *constraint);++/// Get the maximum force that this constraint is allowed to use.+CP_EXPORT cpFloat cpConstraintGetMaxForce(const cpConstraint *constraint);+/// Set the maximum force that this constraint is allowed to use. (defaults to INFINITY)+CP_EXPORT void cpConstraintSetMaxForce(cpConstraint *constraint, cpFloat maxForce);++/// Get rate at which joint error is corrected.+CP_EXPORT cpFloat cpConstraintGetErrorBias(const cpConstraint *constraint);+/// Set rate at which joint error is corrected.+/// Defaults to pow(1.0 - 0.1, 60.0) meaning that it will+/// correct 10% of the error every 1/60th of a second.+CP_EXPORT void cpConstraintSetErrorBias(cpConstraint *constraint, cpFloat errorBias);++/// Get the maximum rate at which joint error is corrected.+CP_EXPORT cpFloat cpConstraintGetMaxBias(const cpConstraint *constraint);+/// Set the maximum rate at which joint error is corrected. (defaults to INFINITY)+CP_EXPORT void cpConstraintSetMaxBias(cpConstraint *constraint, cpFloat maxBias);++/// Get if the two bodies connected by the constraint are allowed to collide or not.+CP_EXPORT cpBool cpConstraintGetCollideBodies(const cpConstraint *constraint);+/// Set if the two bodies connected by the constraint are allowed to collide or not. (defaults to cpFalse)+CP_EXPORT void cpConstraintSetCollideBodies(cpConstraint *constraint, cpBool collideBodies);++/// Get the pre-solve function that is called before the solver runs.+CP_EXPORT cpConstraintPreSolveFunc cpConstraintGetPreSolveFunc(const cpConstraint *constraint);+/// Set the pre-solve function that is called before the solver runs.+CP_EXPORT void cpConstraintSetPreSolveFunc(cpConstraint *constraint, cpConstraintPreSolveFunc preSolveFunc);++/// Get the post-solve function that is called before the solver runs.+CP_EXPORT cpConstraintPostSolveFunc cpConstraintGetPostSolveFunc(const cpConstraint *constraint);+/// Set the post-solve function that is called before the solver runs.+CP_EXPORT void cpConstraintSetPostSolveFunc(cpConstraint *constraint, cpConstraintPostSolveFunc postSolveFunc);++/// Get the user definable data pointer for this constraint+CP_EXPORT cpDataPointer cpConstraintGetUserData(const cpConstraint *constraint);+/// Set the user definable data pointer for this constraint+CP_EXPORT void cpConstraintSetUserData(cpConstraint *constraint, cpDataPointer userData);++/// Get the last impulse applied by this constraint.+CP_EXPORT cpFloat cpConstraintGetImpulse(cpConstraint *constraint);++#include "cpPinJoint.h"+#include "cpSlideJoint.h"+#include "cpPivotJoint.h"+#include "cpGrooveJoint.h"+#include "cpDampedSpring.h"+#include "cpDampedRotarySpring.h"+#include "cpRotaryLimitJoint.h"+#include "cpRatchetJoint.h"+#include "cpGearJoint.h"+#include "cpSimpleMotor.h"++///@}
+ Chipmunk2D-7.0.2/include/chipmunk/cpDampedRotarySpring.h view
@@ -0,0 +1,58 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpDampedRotarySpring cpDampedRotarySpring+/// @{++/// Check if a constraint is a damped rotary springs.+CP_EXPORT cpBool cpConstraintIsDampedRotarySpring(const cpConstraint *constraint);++/// Function type used for damped rotary spring force callbacks.+typedef cpFloat (*cpDampedRotarySpringTorqueFunc)(struct cpConstraint *spring, cpFloat relativeAngle);++/// Allocate a damped rotary spring.+CP_EXPORT cpDampedRotarySpring* cpDampedRotarySpringAlloc(void);+/// Initialize a damped rotary spring.+CP_EXPORT cpDampedRotarySpring* cpDampedRotarySpringInit(cpDampedRotarySpring *joint, cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping);+/// Allocate and initialize a damped rotary spring.+CP_EXPORT cpConstraint* cpDampedRotarySpringNew(cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping);++/// Get the rest length of the spring.+CP_EXPORT cpFloat cpDampedRotarySpringGetRestAngle(const cpConstraint *constraint);+/// Set the rest length of the spring.+CP_EXPORT void cpDampedRotarySpringSetRestAngle(cpConstraint *constraint, cpFloat restAngle);++/// Get the stiffness of the spring in force/distance.+CP_EXPORT cpFloat cpDampedRotarySpringGetStiffness(const cpConstraint *constraint);+/// Set the stiffness of the spring in force/distance.+CP_EXPORT void cpDampedRotarySpringSetStiffness(cpConstraint *constraint, cpFloat stiffness);++/// Get the damping of the spring.+CP_EXPORT cpFloat cpDampedRotarySpringGetDamping(const cpConstraint *constraint);+/// Set the damping of the spring.+CP_EXPORT void cpDampedRotarySpringSetDamping(cpConstraint *constraint, cpFloat damping);++/// Get the damping of the spring.+CP_EXPORT cpDampedRotarySpringTorqueFunc cpDampedRotarySpringGetSpringTorqueFunc(const cpConstraint *constraint);+/// Set the damping of the spring.+CP_EXPORT void cpDampedRotarySpringSetSpringTorqueFunc(cpConstraint *constraint, cpDampedRotarySpringTorqueFunc springTorqueFunc);++/// @}
+ Chipmunk2D-7.0.2/include/chipmunk/cpDampedSpring.h view
@@ -0,0 +1,68 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpDampedSpring cpDampedSpring+/// @{++/// Check if a constraint is a slide joint.+CP_EXPORT cpBool cpConstraintIsDampedSpring(const cpConstraint *constraint);++/// Function type used for damped spring force callbacks.+typedef cpFloat (*cpDampedSpringForceFunc)(cpConstraint *spring, cpFloat dist);++/// Allocate a damped spring.+CP_EXPORT cpDampedSpring* cpDampedSpringAlloc(void);+/// Initialize a damped spring.+CP_EXPORT cpDampedSpring* cpDampedSpringInit(cpDampedSpring *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping);+/// Allocate and initialize a damped spring.+CP_EXPORT cpConstraint* cpDampedSpringNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping);++/// Get the location of the first anchor relative to the first body.+CP_EXPORT cpVect cpDampedSpringGetAnchorA(const cpConstraint *constraint);+/// Set the location of the first anchor relative to the first body.+CP_EXPORT void cpDampedSpringSetAnchorA(cpConstraint *constraint, cpVect anchorA);++/// Get the location of the second anchor relative to the second body.+CP_EXPORT cpVect cpDampedSpringGetAnchorB(const cpConstraint *constraint);+/// Set the location of the second anchor relative to the second body.+CP_EXPORT void cpDampedSpringSetAnchorB(cpConstraint *constraint, cpVect anchorB);++/// Get the rest length of the spring.+CP_EXPORT cpFloat cpDampedSpringGetRestLength(const cpConstraint *constraint);+/// Set the rest length of the spring.+CP_EXPORT void cpDampedSpringSetRestLength(cpConstraint *constraint, cpFloat restLength);++/// Get the stiffness of the spring in force/distance.+CP_EXPORT cpFloat cpDampedSpringGetStiffness(const cpConstraint *constraint);+/// Set the stiffness of the spring in force/distance.+CP_EXPORT void cpDampedSpringSetStiffness(cpConstraint *constraint, cpFloat stiffness);++/// Get the damping of the spring.+CP_EXPORT cpFloat cpDampedSpringGetDamping(const cpConstraint *constraint);+/// Set the damping of the spring.+CP_EXPORT void cpDampedSpringSetDamping(cpConstraint *constraint, cpFloat damping);++/// Get the damping of the spring.+CP_EXPORT cpDampedSpringForceFunc cpDampedSpringGetSpringForceFunc(const cpConstraint *constraint);+/// Set the damping of the spring.+CP_EXPORT void cpDampedSpringSetSpringForceFunc(cpConstraint *constraint, cpDampedSpringForceFunc springForceFunc);++/// @}
+ Chipmunk2D-7.0.2/include/chipmunk/cpGearJoint.h view
@@ -0,0 +1,45 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpGearJoint cpGearJoint+/// @{++/// Check if a constraint is a damped rotary springs.+CP_EXPORT cpBool cpConstraintIsGearJoint(const cpConstraint *constraint);++/// Allocate a gear joint.+CP_EXPORT cpGearJoint* cpGearJointAlloc(void);+/// Initialize a gear joint.+CP_EXPORT cpGearJoint* cpGearJointInit(cpGearJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio);+/// Allocate and initialize a gear joint.+CP_EXPORT cpConstraint* cpGearJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio);++/// Get the phase offset of the gears.+CP_EXPORT cpFloat cpGearJointGetPhase(const cpConstraint *constraint);+/// Set the phase offset of the gears.+CP_EXPORT void cpGearJointSetPhase(cpConstraint *constraint, cpFloat phase);++/// Get the angular distance of each ratchet.+CP_EXPORT cpFloat cpGearJointGetRatio(const cpConstraint *constraint);+/// Set the ratio of a gear joint.+CP_EXPORT void cpGearJointSetRatio(cpConstraint *constraint, cpFloat ratio);++/// @}
+ Chipmunk2D-7.0.2/include/chipmunk/cpGrooveJoint.h view
@@ -0,0 +1,50 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpGrooveJoint cpGrooveJoint+/// @{++/// Check if a constraint is a slide joint.+CP_EXPORT cpBool cpConstraintIsGrooveJoint(const cpConstraint *constraint);++/// Allocate a groove joint.+CP_EXPORT cpGrooveJoint* cpGrooveJointAlloc(void);+/// Initialize a groove joint.+CP_EXPORT cpGrooveJoint* cpGrooveJointInit(cpGrooveJoint *joint, cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB);+/// Allocate and initialize a groove joint.+CP_EXPORT cpConstraint* cpGrooveJointNew(cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB);++/// Get the first endpoint of the groove relative to the first body.+CP_EXPORT cpVect cpGrooveJointGetGrooveA(const cpConstraint *constraint);+/// Set the first endpoint of the groove relative to the first body.+CP_EXPORT void cpGrooveJointSetGrooveA(cpConstraint *constraint, cpVect grooveA);++/// Get the first endpoint of the groove relative to the first body.+CP_EXPORT cpVect cpGrooveJointGetGrooveB(const cpConstraint *constraint);+/// Set the first endpoint of the groove relative to the first body.+CP_EXPORT void cpGrooveJointSetGrooveB(cpConstraint *constraint, cpVect grooveB);++/// Get the location of the second anchor relative to the second body.+CP_EXPORT cpVect cpGrooveJointGetAnchorB(const cpConstraint *constraint);+/// Set the location of the second anchor relative to the second body.+CP_EXPORT void cpGrooveJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);++/// @}
+ Chipmunk2D-7.0.2/include/chipmunk/cpHastySpace.h view
@@ -0,0 +1,27 @@+// Copyright 2013 Howling Moon Software. All rights reserved.+// See http://chipmunk2d.net/legal.php for more information.++/// cpHastySpace is exclusive to Chipmunk Pro+/// Currently it enables ARM NEON optimizations in the solver, but in the future will include other optimizations such as+/// a multi-threaded solver and multi-threaded collision broadphases.++struct cpHastySpace;+typedef struct cpHastySpace cpHastySpace;++/// Create a new hasty space.+/// On ARM platforms that support NEON, this will enable the vectorized solver.+/// cpHastySpace also supports multiple threads, but runs single threaded by default for determinism.+CP_EXPORT cpSpace *cpHastySpaceNew(void);+CP_EXPORT void cpHastySpaceFree(cpSpace *space);++/// Set the number of threads to use for the solver.+/// Currently Chipmunk is limited to 2 threads as using more generally provides very minimal performance gains.+/// Passing 0 as the thread count on iOS or OS X will cause Chipmunk to automatically detect the number of threads it should use.+/// On other platforms passing 0 for the thread count will set 1 thread.+CP_EXPORT void cpHastySpaceSetThreads(cpSpace *space, unsigned long threads);++/// Returns the number of threads the solver is using to run.+CP_EXPORT unsigned long cpHastySpaceGetThreads(cpSpace *space);++/// When stepping a hasty space, you must use this function.+CP_EXPORT void cpHastySpaceStep(cpSpace *space, cpFloat dt);
+ Chipmunk2D-7.0.2/include/chipmunk/cpMarch.h view
@@ -0,0 +1,28 @@+// Copyright 2013 Howling Moon Software. All rights reserved.+// See http://chipmunk2d.net/legal.php for more information.++/// Function type used as a callback from the marching squares algorithm to sample an image function.+/// It passes you the point to sample and your context pointer, and you return the density.+typedef cpFloat (*cpMarchSampleFunc)(cpVect point, void *data);++/// Function type used as a callback from the marching squares algorithm to output a line segment.+/// It passes you the two endpoints and your context pointer.+typedef void (*cpMarchSegmentFunc)(cpVect v0, cpVect v1, void *data);++/// Trace an anti-aliased contour of an image along a particular threshold.+/// The given number of samples will be taken and spread across the bounding box area using the sampling function and context.+/// The segment function will be called for each segment detected that lies along the density contour for @c threshold.+CP_EXPORT void cpMarchSoft(+ cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat threshold,+ cpMarchSegmentFunc segment, void *segment_data,+ cpMarchSampleFunc sample, void *sample_data+);++/// Trace an aliased curve of an image along a particular threshold.+/// The given number of samples will be taken and spread across the bounding box area using the sampling function and context.+/// The segment function will be called for each segment detected that lies along the density contour for @c threshold.+CP_EXPORT void cpMarchHard(+ cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat threshold,+ cpMarchSegmentFunc segment, void *segment_data,+ cpMarchSampleFunc sample, void *sample_data+);
+ Chipmunk2D-7.0.2/include/chipmunk/cpPinJoint.h view
@@ -0,0 +1,50 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpPinJoint cpPinJoint+/// @{++/// Check if a constraint is a pin joint.+CP_EXPORT cpBool cpConstraintIsPinJoint(const cpConstraint *constraint);++/// Allocate a pin joint.+CP_EXPORT cpPinJoint* cpPinJointAlloc(void);+/// Initialize a pin joint.+CP_EXPORT cpPinJoint* cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);+/// Allocate and initialize a pin joint.+CP_EXPORT cpConstraint* cpPinJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);++/// Get the location of the first anchor relative to the first body.+CP_EXPORT cpVect cpPinJointGetAnchorA(const cpConstraint *constraint);+/// Set the location of the first anchor relative to the first body.+CP_EXPORT void cpPinJointSetAnchorA(cpConstraint *constraint, cpVect anchorA);++/// Get the location of the second anchor relative to the second body.+CP_EXPORT cpVect cpPinJointGetAnchorB(const cpConstraint *constraint);+/// Set the location of the second anchor relative to the second body.+CP_EXPORT void cpPinJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);++/// Get the distance the joint will maintain between the two anchors.+CP_EXPORT cpFloat cpPinJointGetDist(const cpConstraint *constraint);+/// Set the distance the joint will maintain between the two anchors.+CP_EXPORT void cpPinJointSetDist(cpConstraint *constraint, cpFloat dist);++///@}
+ Chipmunk2D-7.0.2/include/chipmunk/cpPivotJoint.h view
@@ -0,0 +1,47 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpPivotJoint cpPivotJoint+/// @{++/// Check if a constraint is a slide joint.+CP_EXPORT cpBool cpConstraintIsPivotJoint(const cpConstraint *constraint);++/// Allocate a pivot joint+CP_EXPORT cpPivotJoint* cpPivotJointAlloc(void);+/// Initialize a pivot joint.+CP_EXPORT cpPivotJoint* cpPivotJointInit(cpPivotJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);+/// Allocate and initialize a pivot joint.+CP_EXPORT cpConstraint* cpPivotJointNew(cpBody *a, cpBody *b, cpVect pivot);+/// Allocate and initialize a pivot joint with specific anchors.+CP_EXPORT cpConstraint* cpPivotJointNew2(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);++/// Get the location of the first anchor relative to the first body.+CP_EXPORT cpVect cpPivotJointGetAnchorA(const cpConstraint *constraint);+/// Set the location of the first anchor relative to the first body.+CP_EXPORT void cpPivotJointSetAnchorA(cpConstraint *constraint, cpVect anchorA);++/// Get the location of the second anchor relative to the second body.+CP_EXPORT cpVect cpPivotJointGetAnchorB(const cpConstraint *constraint);+/// Set the location of the second anchor relative to the second body.+CP_EXPORT void cpPivotJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);++/// @}
+ Chipmunk2D-7.0.2/include/chipmunk/cpPolyShape.h view
@@ -0,0 +1,56 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpPolyShape cpPolyShape+/// @{++/// Allocate a polygon shape.+CP_EXPORT cpPolyShape* cpPolyShapeAlloc(void);+/// Initialize a polygon shape with rounded corners.+/// A convex hull will be created from the vertexes.+CP_EXPORT cpPolyShape* cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius);+/// Initialize a polygon shape with rounded corners.+/// The vertexes must be convex with a counter-clockwise winding.+CP_EXPORT cpPolyShape* cpPolyShapeInitRaw(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpFloat radius);+/// Allocate and initialize a polygon shape with rounded corners.+/// A convex hull will be created from the vertexes.+CP_EXPORT cpShape* cpPolyShapeNew(cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius);+/// Allocate and initialize a polygon shape with rounded corners.+/// The vertexes must be convex with a counter-clockwise winding.+CP_EXPORT cpShape* cpPolyShapeNewRaw(cpBody *body, int count, const cpVect *verts, cpFloat radius);++/// Initialize a box shaped polygon shape with rounded corners.+CP_EXPORT cpPolyShape* cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height, cpFloat radius);+/// Initialize an offset box shaped polygon shape with rounded corners.+CP_EXPORT cpPolyShape* cpBoxShapeInit2(cpPolyShape *poly, cpBody *body, cpBB box, cpFloat radius);+/// Allocate and initialize a box shaped polygon shape.+CP_EXPORT cpShape* cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height, cpFloat radius);+/// Allocate and initialize an offset box shaped polygon shape.+CP_EXPORT cpShape* cpBoxShapeNew2(cpBody *body, cpBB box, cpFloat radius);++/// Get the number of verts in a polygon shape.+CP_EXPORT int cpPolyShapeGetCount(const cpShape *shape);+/// Get the @c ith vertex of a polygon shape.+CP_EXPORT cpVect cpPolyShapeGetVert(const cpShape *shape, int index);+/// Get the radius of a polygon shape.+CP_EXPORT cpFloat cpPolyShapeGetRadius(const cpShape *shape);++/// @}
+ Chipmunk2D-7.0.2/include/chipmunk/cpPolyline.h view
@@ -0,0 +1,70 @@+// Copyright 2013 Howling Moon Software. All rights reserved.+// See http://chipmunk2d.net/legal.php for more information.++// Polylines are just arrays of vertexes.+// They are looped if the first vertex is equal to the last.+// cpPolyline structs are intended to be passed by value and destroyed when you are done with them.+typedef struct cpPolyline {+ int count, capacity;+ cpVect verts[];+} cpPolyline;++/// Destroy and free a polyline instance.+CP_EXPORT void cpPolylineFree(cpPolyline *line);++/// Returns true if the first vertex is equal to the last.+CP_EXPORT cpBool cpPolylineIsClosed(cpPolyline *line);++/**+ Returns a copy of a polyline simplified by using the Douglas-Peucker algorithm.+ This works very well on smooth or gently curved shapes, but not well on straight edged or angular shapes.+*/+CP_EXPORT cpPolyline *cpPolylineSimplifyCurves(cpPolyline *line, cpFloat tol);++/**+ Returns a copy of a polyline simplified by discarding "flat" vertexes.+ This works well on straigt edged or angular shapes, not as well on smooth shapes.+*/+CP_EXPORT cpPolyline *cpPolylineSimplifyVertexes(cpPolyline *line, cpFloat tol);++/// Get the convex hull of a polyline as a looped polyline.+CP_EXPORT cpPolyline *cpPolylineToConvexHull(cpPolyline *line, cpFloat tol);+++/// Polyline sets are collections of polylines, generally built by cpMarchSoft() or cpMarchHard().+typedef struct cpPolylineSet {+ int count, capacity;+ cpPolyline **lines;+} cpPolylineSet;++/// Allocate a new polyline set.+CP_EXPORT cpPolylineSet *cpPolylineSetAlloc(void);++/// Initialize a new polyline set.+CP_EXPORT cpPolylineSet *cpPolylineSetInit(cpPolylineSet *set);++/// Allocate and initialize a polyline set.+CP_EXPORT cpPolylineSet *cpPolylineSetNew(void);++/// Destroy a polyline set.+CP_EXPORT void cpPolylineSetDestroy(cpPolylineSet *set, cpBool freePolylines);++/// Destroy and free a polyline set.+CP_EXPORT void cpPolylineSetFree(cpPolylineSet *set, cpBool freePolylines);++/**+ Add a line segment to a polyline set.+ A segment will either start a new polyline, join two others, or add to or loop an existing polyline.+ This is mostly intended to be used as a callback directly from cpMarchSoft() or cpMarchHard().+*/+CP_EXPORT void cpPolylineSetCollectSegment(cpVect v0, cpVect v1, cpPolylineSet *lines);++/**+ Get an approximate convex decomposition from a polyline.+ Returns a cpPolylineSet of convex hulls that match the original shape to within 'tol'.+ NOTE: If the input is a self intersecting polygon, the output might end up overly simplified.+*/++CP_EXPORT cpPolylineSet *cpPolylineConvexDecomposition(cpPolyline *line, cpFloat tol);++#define cpPolylineConvexDecomposition_BETA cpPolylineConvexDecomposition
+ Chipmunk2D-7.0.2/include/chipmunk/cpRatchetJoint.h view
@@ -0,0 +1,50 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpRatchetJoint cpRatchetJoint+/// @{++/// Check if a constraint is a damped rotary springs.+CP_EXPORT cpBool cpConstraintIsRatchetJoint(const cpConstraint *constraint);++/// Allocate a ratchet joint.+CP_EXPORT cpRatchetJoint* cpRatchetJointAlloc(void);+/// Initialize a ratched joint.+CP_EXPORT cpRatchetJoint* cpRatchetJointInit(cpRatchetJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet);+/// Allocate and initialize a ratchet joint.+CP_EXPORT cpConstraint* cpRatchetJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet);++/// Get the angle of the current ratchet tooth.+CP_EXPORT cpFloat cpRatchetJointGetAngle(const cpConstraint *constraint);+/// Set the angle of the current ratchet tooth.+CP_EXPORT void cpRatchetJointSetAngle(cpConstraint *constraint, cpFloat angle);++/// Get the phase offset of the ratchet.+CP_EXPORT cpFloat cpRatchetJointGetPhase(const cpConstraint *constraint);+/// Get the phase offset of the ratchet.+CP_EXPORT void cpRatchetJointSetPhase(cpConstraint *constraint, cpFloat phase);++/// Get the angular distance of each ratchet.+CP_EXPORT cpFloat cpRatchetJointGetRatchet(const cpConstraint *constraint);+/// Set the angular distance of each ratchet.+CP_EXPORT void cpRatchetJointSetRatchet(cpConstraint *constraint, cpFloat ratchet);++/// @}
+ Chipmunk2D-7.0.2/include/chipmunk/cpRobust.h view
@@ -0,0 +1,11 @@+#include "chipmunk/cpVect.h"++// This is a private header for functions (currently just one) that need strict floating point results.+// It was easier to put this in it's own file than to fiddle with 4 different compiler specific pragmas or attributes.+// "Fast math" should be disabled here.++// Check if c is to the left of segment (a, b).+cpBool cpCheckPointGreater(const cpVect a, const cpVect b, const cpVect c);++// Check if p is behind one of v0 or v1 on axis n.+cpBool cpCheckAxis(cpVect v0, cpVect v1, cpVect p, cpVect n);
+ Chipmunk2D-7.0.2/include/chipmunk/cpRotaryLimitJoint.h view
@@ -0,0 +1,45 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpRotaryLimitJoint cpRotaryLimitJoint+/// @{++/// Check if a constraint is a damped rotary springs.+CP_EXPORT cpBool cpConstraintIsRotaryLimitJoint(const cpConstraint *constraint);++/// Allocate a damped rotary limit joint.+CP_EXPORT cpRotaryLimitJoint* cpRotaryLimitJointAlloc(void);+/// Initialize a damped rotary limit joint.+CP_EXPORT cpRotaryLimitJoint* cpRotaryLimitJointInit(cpRotaryLimitJoint *joint, cpBody *a, cpBody *b, cpFloat min, cpFloat max);+/// Allocate and initialize a damped rotary limit joint.+CP_EXPORT cpConstraint* cpRotaryLimitJointNew(cpBody *a, cpBody *b, cpFloat min, cpFloat max);++/// Get the minimum distance the joint will maintain between the two anchors.+CP_EXPORT cpFloat cpRotaryLimitJointGetMin(const cpConstraint *constraint);+/// Set the minimum distance the joint will maintain between the two anchors.+CP_EXPORT void cpRotaryLimitJointSetMin(cpConstraint *constraint, cpFloat min);++/// Get the maximum distance the joint will maintain between the two anchors.+CP_EXPORT cpFloat cpRotaryLimitJointGetMax(const cpConstraint *constraint);+/// Set the maximum distance the joint will maintain between the two anchors.+CP_EXPORT void cpRotaryLimitJointSetMax(cpConstraint *constraint, cpFloat max);++/// @}
+ Chipmunk2D-7.0.2/include/chipmunk/cpShape.h view
@@ -0,0 +1,199 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpShape cpShape+/// The cpShape struct defines the shape of a rigid body.+/// @{++/// Point query info struct.+typedef struct cpPointQueryInfo {+ /// The nearest shape, NULL if no shape was within range.+ const cpShape *shape;+ /// The closest point on the shape's surface. (in world space coordinates)+ cpVect point;+ /// The distance to the point. The distance is negative if the point is inside the shape.+ cpFloat distance;+ /// The gradient of the signed distance function.+ /// The value should be similar to info.p/info.d, but accurate even for very small values of info.d.+ cpVect gradient;+} cpPointQueryInfo;++/// Segment query info struct.+typedef struct cpSegmentQueryInfo {+ /// The shape that was hit, or NULL if no collision occured.+ const cpShape *shape;+ /// The point of impact.+ cpVect point;+ /// The normal of the surface hit.+ cpVect normal;+ /// The normalized distance along the query segment in the range [0, 1].+ cpFloat alpha;+} cpSegmentQueryInfo;++/// Fast collision filtering type that is used to determine if two objects collide before calling collision or query callbacks.+typedef struct cpShapeFilter {+ /// Two objects with the same non-zero group value do not collide.+ /// This is generally used to group objects in a composite object together to disable self collisions.+ cpGroup group;+ /// A bitmask of user definable categories that this object belongs to.+ /// The category/mask combinations of both objects in a collision must agree for a collision to occur.+ cpBitmask categories;+ /// A bitmask of user definable category types that this object object collides with.+ /// The category/mask combinations of both objects in a collision must agree for a collision to occur.+ cpBitmask mask;+} cpShapeFilter;++/// Collision filter value for a shape that will collide with anything except CP_SHAPE_FILTER_NONE.+static const cpShapeFilter CP_SHAPE_FILTER_ALL = {CP_NO_GROUP, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES};+/// Collision filter value for a shape that does not collide with anything.+static const cpShapeFilter CP_SHAPE_FILTER_NONE = {CP_NO_GROUP, ~CP_ALL_CATEGORIES, ~CP_ALL_CATEGORIES};++/// Create a new collision filter.+static inline cpShapeFilter+cpShapeFilterNew(cpGroup group, cpBitmask categories, cpBitmask mask)+{+ cpShapeFilter filter = {group, categories, mask};+ return filter;+}++/// Destroy a shape.+CP_EXPORT void cpShapeDestroy(cpShape *shape);+/// Destroy and Free a shape.+CP_EXPORT void cpShapeFree(cpShape *shape);++/// Update, cache and return the bounding box of a shape based on the body it's attached to.+CP_EXPORT cpBB cpShapeCacheBB(cpShape *shape);+/// Update, cache and return the bounding box of a shape with an explicit transformation.+CP_EXPORT cpBB cpShapeUpdate(cpShape *shape, cpTransform transform);++/// Perform a nearest point query. It finds the closest point on the surface of shape to a specific point.+/// The value returned is the distance between the points. A negative distance means the point is inside the shape.+CP_EXPORT cpFloat cpShapePointQuery(const cpShape *shape, cpVect p, cpPointQueryInfo *out);++/// Perform a segment query against a shape. @c info must be a pointer to a valid cpSegmentQueryInfo structure.+CP_EXPORT cpBool cpShapeSegmentQuery(const cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info);++/// Return contact information about two shapes.+CP_EXPORT cpContactPointSet cpShapesCollide(const cpShape *a, const cpShape *b);++/// The cpSpace this body is added to.+CP_EXPORT cpSpace* cpShapeGetSpace(const cpShape *shape);++/// The cpBody this shape is connected to.+CP_EXPORT cpBody* cpShapeGetBody(const cpShape *shape);+/// Set the cpBody this shape is connected to.+/// Can only be used if the shape is not currently added to a space.+CP_EXPORT void cpShapeSetBody(cpShape *shape, cpBody *body);++/// Get the mass of the shape if you are having Chipmunk calculate mass properties for you.+CP_EXPORT cpFloat cpShapeGetMass(cpShape *shape);+/// Set the mass of this shape to have Chipmunk calculate mass properties for you.+CP_EXPORT void cpShapeSetMass(cpShape *shape, cpFloat mass);++/// Get the density of the shape if you are having Chipmunk calculate mass properties for you.+CP_EXPORT cpFloat cpShapeGetDensity(cpShape *shape);+/// Set the density of this shape to have Chipmunk calculate mass properties for you.+CP_EXPORT void cpShapeSetDensity(cpShape *shape, cpFloat density);++/// Get the calculated moment of inertia for this shape.+CP_EXPORT cpFloat cpShapeGetMoment(cpShape *shape);+/// Get the calculated area of this shape.+CP_EXPORT cpFloat cpShapeGetArea(cpShape *shape);+/// Get the centroid of this shape.+CP_EXPORT cpVect cpShapeGetCenterOfGravity(cpShape *shape);++/// Get the bounding box that contains the shape given it's current position and angle.+CP_EXPORT cpBB cpShapeGetBB(const cpShape *shape);++/// Get if the shape is set to be a sensor or not.+CP_EXPORT cpBool cpShapeGetSensor(const cpShape *shape);+/// Set if the shape is a sensor or not.+CP_EXPORT void cpShapeSetSensor(cpShape *shape, cpBool sensor);++/// Get the elasticity of this shape.+CP_EXPORT cpFloat cpShapeGetElasticity(const cpShape *shape);+/// Set the elasticity of this shape.+CP_EXPORT void cpShapeSetElasticity(cpShape *shape, cpFloat elasticity);++/// Get the friction of this shape.+CP_EXPORT cpFloat cpShapeGetFriction(const cpShape *shape);+/// Set the friction of this shape.+CP_EXPORT void cpShapeSetFriction(cpShape *shape, cpFloat friction);++/// Get the surface velocity of this shape.+CP_EXPORT cpVect cpShapeGetSurfaceVelocity(const cpShape *shape);+/// Set the surface velocity of this shape.+CP_EXPORT void cpShapeSetSurfaceVelocity(cpShape *shape, cpVect surfaceVelocity);++/// Get the user definable data pointer of this shape.+CP_EXPORT cpDataPointer cpShapeGetUserData(const cpShape *shape);+/// Set the user definable data pointer of this shape.+CP_EXPORT void cpShapeSetUserData(cpShape *shape, cpDataPointer userData);++/// Set the collision type of this shape.+CP_EXPORT cpCollisionType cpShapeGetCollisionType(const cpShape *shape);+/// Get the collision type of this shape.+CP_EXPORT void cpShapeSetCollisionType(cpShape *shape, cpCollisionType collisionType);++/// Get the collision filtering parameters of this shape.+CP_EXPORT cpShapeFilter cpShapeGetFilter(const cpShape *shape);+/// Set the collision filtering parameters of this shape.+CP_EXPORT void cpShapeSetFilter(cpShape *shape, cpShapeFilter filter);+++/// @}+/// @defgroup cpCircleShape cpCircleShape++/// Allocate a circle shape.+CP_EXPORT cpCircleShape* cpCircleShapeAlloc(void);+/// Initialize a circle shape.+CP_EXPORT cpCircleShape* cpCircleShapeInit(cpCircleShape *circle, cpBody *body, cpFloat radius, cpVect offset);+/// Allocate and initialize a circle shape.+CP_EXPORT cpShape* cpCircleShapeNew(cpBody *body, cpFloat radius, cpVect offset);++/// Get the offset of a circle shape.+CP_EXPORT cpVect cpCircleShapeGetOffset(const cpShape *shape);+/// Get the radius of a circle shape.+CP_EXPORT cpFloat cpCircleShapeGetRadius(const cpShape *shape);++/// @}+/// @defgroup cpSegmentShape cpSegmentShape++/// Allocate a segment shape.+CP_EXPORT cpSegmentShape* cpSegmentShapeAlloc(void);+/// Initialize a segment shape.+CP_EXPORT cpSegmentShape* cpSegmentShapeInit(cpSegmentShape *seg, cpBody *body, cpVect a, cpVect b, cpFloat radius);+/// Allocate and initialize a segment shape.+CP_EXPORT cpShape* cpSegmentShapeNew(cpBody *body, cpVect a, cpVect b, cpFloat radius);++/// Let Chipmunk know about the geometry of adjacent segments to avoid colliding with endcaps.+CP_EXPORT void cpSegmentShapeSetNeighbors(cpShape *shape, cpVect prev, cpVect next);++/// Get the first endpoint of a segment shape.+CP_EXPORT cpVect cpSegmentShapeGetA(const cpShape *shape);+/// Get the second endpoint of a segment shape.+CP_EXPORT cpVect cpSegmentShapeGetB(const cpShape *shape);+/// Get the normal of a segment shape.+CP_EXPORT cpVect cpSegmentShapeGetNormal(const cpShape *shape);+/// Get the first endpoint of a segment shape.+CP_EXPORT cpFloat cpSegmentShapeGetRadius(const cpShape *shape);++/// @}
+ Chipmunk2D-7.0.2/include/chipmunk/cpSimpleMotor.h view
@@ -0,0 +1,43 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpSimpleMotor cpSimpleMotor+/// @{++/// Opaque struct type for damped rotary springs.+typedef struct cpSimpleMotor cpSimpleMotor;++/// Check if a constraint is a damped rotary springs.+CP_EXPORT cpBool cpConstraintIsSimpleMotor(const cpConstraint *constraint);++/// Allocate a simple motor.+CP_EXPORT cpSimpleMotor* cpSimpleMotorAlloc(void);+/// initialize a simple motor.+CP_EXPORT cpSimpleMotor* cpSimpleMotorInit(cpSimpleMotor *joint, cpBody *a, cpBody *b, cpFloat rate);+/// Allocate and initialize a simple motor.+CP_EXPORT cpConstraint* cpSimpleMotorNew(cpBody *a, cpBody *b, cpFloat rate);++/// Get the rate of the motor.+CP_EXPORT cpFloat cpSimpleMotorGetRate(const cpConstraint *constraint);+/// Set the rate of the motor.+CP_EXPORT void cpSimpleMotorSetRate(cpConstraint *constraint, cpFloat rate);++/// @}
+ Chipmunk2D-7.0.2/include/chipmunk/cpSlideJoint.h view
@@ -0,0 +1,55 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpSlideJoint cpSlideJoint+/// @{++/// Check if a constraint is a slide joint.+CP_EXPORT cpBool cpConstraintIsSlideJoint(const cpConstraint *constraint);++/// Allocate a slide joint.+CP_EXPORT cpSlideJoint* cpSlideJointAlloc(void);+/// Initialize a slide joint.+CP_EXPORT cpSlideJoint* cpSlideJointInit(cpSlideJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max);+/// Allocate and initialize a slide joint.+CP_EXPORT cpConstraint* cpSlideJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max);++/// Get the location of the first anchor relative to the first body.+CP_EXPORT cpVect cpSlideJointGetAnchorA(const cpConstraint *constraint);+/// Set the location of the first anchor relative to the first body.+CP_EXPORT void cpSlideJointSetAnchorA(cpConstraint *constraint, cpVect anchorA);++/// Get the location of the second anchor relative to the second body.+CP_EXPORT cpVect cpSlideJointGetAnchorB(const cpConstraint *constraint);+/// Set the location of the second anchor relative to the second body.+CP_EXPORT void cpSlideJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);++/// Get the minimum distance the joint will maintain between the two anchors.+CP_EXPORT cpFloat cpSlideJointGetMin(const cpConstraint *constraint);+/// Set the minimum distance the joint will maintain between the two anchors.+CP_EXPORT void cpSlideJointSetMin(cpConstraint *constraint, cpFloat min);++/// Get the maximum distance the joint will maintain between the two anchors.+CP_EXPORT cpFloat cpSlideJointGetMax(const cpConstraint *constraint);+/// Set the maximum distance the joint will maintain between the two anchors.+CP_EXPORT void cpSlideJointSetMax(cpConstraint *constraint, cpFloat max);++/// @}
+ Chipmunk2D-7.0.2/include/chipmunk/cpSpace.h view
@@ -0,0 +1,319 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/// @defgroup cpSpace cpSpace+/// @{++//MARK: Definitions++/// Collision begin event function callback type.+/// Returning false from a begin callback causes the collision to be ignored until+/// the the separate callback is called when the objects stop colliding.+typedef cpBool (*cpCollisionBeginFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData);+/// Collision pre-solve event function callback type.+/// Returning false from a pre-step callback causes the collision to be ignored until the next step.+typedef cpBool (*cpCollisionPreSolveFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData);+/// Collision post-solve event function callback type.+typedef void (*cpCollisionPostSolveFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData);+/// Collision separate event function callback type.+typedef void (*cpCollisionSeparateFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData);++/// Struct that holds function callback pointers to configure custom collision handling.+/// Collision handlers have a pair of types; when a collision occurs between two shapes that have these types, the collision handler functions are triggered.+struct cpCollisionHandler {+ /// Collision type identifier of the first shape that this handler recognizes.+ /// In the collision handler callback, the shape with this type will be the first argument. Read only.+ const cpCollisionType typeA;+ /// Collision type identifier of the second shape that this handler recognizes.+ /// In the collision handler callback, the shape with this type will be the second argument. Read only.+ const cpCollisionType typeB;+ /// This function is called when two shapes with types that match this collision handler begin colliding.+ cpCollisionBeginFunc beginFunc;+ /// This function is called each step when two shapes with types that match this collision handler are colliding.+ /// It's called before the collision solver runs so that you can affect a collision's outcome.+ cpCollisionPreSolveFunc preSolveFunc;+ /// This function is called each step when two shapes with types that match this collision handler are colliding.+ /// It's called after the collision solver runs so that you can read back information about the collision to trigger events in your game.+ cpCollisionPostSolveFunc postSolveFunc;+ /// This function is called when two shapes with types that match this collision handler stop colliding.+ cpCollisionSeparateFunc separateFunc;+ /// This is a user definable context pointer that is passed to all of the collision handler functions.+ cpDataPointer userData;+};++// TODO: Make timestep a parameter?+++//MARK: Memory and Initialization++/// Allocate a cpSpace.+CP_EXPORT cpSpace* cpSpaceAlloc(void);+/// Initialize a cpSpace.+CP_EXPORT cpSpace* cpSpaceInit(cpSpace *space);+/// Allocate and initialize a cpSpace.+CP_EXPORT cpSpace* cpSpaceNew(void);++/// Destroy a cpSpace.+CP_EXPORT void cpSpaceDestroy(cpSpace *space);+/// Destroy and free a cpSpace.+CP_EXPORT void cpSpaceFree(cpSpace *space);+++//MARK: Properties++/// Number of iterations to use in the impulse solver to solve contacts and other constraints.+CP_EXPORT int cpSpaceGetIterations(const cpSpace *space);+CP_EXPORT void cpSpaceSetIterations(cpSpace *space, int iterations);++/// Gravity to pass to rigid bodies when integrating velocity.+CP_EXPORT cpVect cpSpaceGetGravity(const cpSpace *space);+CP_EXPORT void cpSpaceSetGravity(cpSpace *space, cpVect gravity);++/// Damping rate expressed as the fraction of velocity bodies retain each second.+/// A value of 0.9 would mean that each body's velocity will drop 10% per second.+/// The default value is 1.0, meaning no damping is applied.+/// @note This damping value is different than those of cpDampedSpring and cpDampedRotarySpring.+CP_EXPORT cpFloat cpSpaceGetDamping(const cpSpace *space);+CP_EXPORT void cpSpaceSetDamping(cpSpace *space, cpFloat damping);++/// Speed threshold for a body to be considered idle.+/// The default value of 0 means to let the space guess a good threshold based on gravity.+CP_EXPORT cpFloat cpSpaceGetIdleSpeedThreshold(const cpSpace *space);+CP_EXPORT void cpSpaceSetIdleSpeedThreshold(cpSpace *space, cpFloat idleSpeedThreshold);++/// Time a group of bodies must remain idle in order to fall asleep.+/// Enabling sleeping also implicitly enables the the contact graph.+/// The default value of INFINITY disables the sleeping algorithm.+CP_EXPORT cpFloat cpSpaceGetSleepTimeThreshold(const cpSpace *space);+CP_EXPORT void cpSpaceSetSleepTimeThreshold(cpSpace *space, cpFloat sleepTimeThreshold);++/// Amount of encouraged penetration between colliding shapes.+/// Used to reduce oscillating contacts and keep the collision cache warm.+/// Defaults to 0.1. If you have poor simulation quality,+/// increase this number as much as possible without allowing visible amounts of overlap.+CP_EXPORT cpFloat cpSpaceGetCollisionSlop(const cpSpace *space);+CP_EXPORT void cpSpaceSetCollisionSlop(cpSpace *space, cpFloat collisionSlop);++/// Determines how fast overlapping shapes are pushed apart.+/// Expressed as a fraction of the error remaining after each second.+/// Defaults to pow(1.0 - 0.1, 60.0) meaning that Chipmunk fixes 10% of overlap each frame at 60Hz.+CP_EXPORT cpFloat cpSpaceGetCollisionBias(const cpSpace *space);+CP_EXPORT void cpSpaceSetCollisionBias(cpSpace *space, cpFloat collisionBias);++/// Number of frames that contact information should persist.+/// Defaults to 3. There is probably never a reason to change this value.+CP_EXPORT cpTimestamp cpSpaceGetCollisionPersistence(const cpSpace *space);+CP_EXPORT void cpSpaceSetCollisionPersistence(cpSpace *space, cpTimestamp collisionPersistence);++/// User definable data pointer.+/// Generally this points to your game's controller or game state+/// class so you can access it when given a cpSpace reference in a callback.+CP_EXPORT cpDataPointer cpSpaceGetUserData(const cpSpace *space);+CP_EXPORT void cpSpaceSetUserData(cpSpace *space, cpDataPointer userData);++/// The Space provided static body for a given cpSpace.+/// This is merely provided for convenience and you are not required to use it.+CP_EXPORT cpBody* cpSpaceGetStaticBody(const cpSpace *space);++/// Returns the current (or most recent) time step used with the given space.+/// Useful from callbacks if your time step is not a compile-time global.+CP_EXPORT cpFloat cpSpaceGetCurrentTimeStep(const cpSpace *space);++/// returns true from inside a callback when objects cannot be added/removed.+CP_EXPORT cpBool cpSpaceIsLocked(cpSpace *space);+++//MARK: Collision Handlers++/// Create or return the existing collision handler that is called for all collisions that are not handled by a more specific collision handler.+CP_EXPORT cpCollisionHandler *cpSpaceAddDefaultCollisionHandler(cpSpace *space);+/// Create or return the existing collision handler for the specified pair of collision types.+/// If wildcard handlers are used with either of the collision types, it's the responibility of the custom handler to invoke the wildcard handlers.+CP_EXPORT cpCollisionHandler *cpSpaceAddCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b);+/// Create or return the existing wildcard collision handler for the specified type.+CP_EXPORT cpCollisionHandler *cpSpaceAddWildcardHandler(cpSpace *space, cpCollisionType type);+++//MARK: Add/Remove objects++/// Add a collision shape to the simulation.+/// If the shape is attached to a static body, it will be added as a static shape.+CP_EXPORT cpShape* cpSpaceAddShape(cpSpace *space, cpShape *shape);+/// Add a rigid body to the simulation.+CP_EXPORT cpBody* cpSpaceAddBody(cpSpace *space, cpBody *body);+/// Add a constraint to the simulation.+CP_EXPORT cpConstraint* cpSpaceAddConstraint(cpSpace *space, cpConstraint *constraint);++/// Remove a collision shape from the simulation.+CP_EXPORT void cpSpaceRemoveShape(cpSpace *space, cpShape *shape);+/// Remove a rigid body from the simulation.+CP_EXPORT void cpSpaceRemoveBody(cpSpace *space, cpBody *body);+/// Remove a constraint from the simulation.+CP_EXPORT void cpSpaceRemoveConstraint(cpSpace *space, cpConstraint *constraint);++/// Test if a collision shape has been added to the space.+CP_EXPORT cpBool cpSpaceContainsShape(cpSpace *space, cpShape *shape);+/// Test if a rigid body has been added to the space.+CP_EXPORT cpBool cpSpaceContainsBody(cpSpace *space, cpBody *body);+/// Test if a constraint has been added to the space.+CP_EXPORT cpBool cpSpaceContainsConstraint(cpSpace *space, cpConstraint *constraint);++//MARK: Post-Step Callbacks++/// Post Step callback function type.+typedef void (*cpPostStepFunc)(cpSpace *space, void *key, void *data);+/// Schedule a post-step callback to be called when cpSpaceStep() finishes.+/// You can only register one callback per unique value for @c key.+/// Returns true only if @c key has never been scheduled before.+/// It's possible to pass @c NULL for @c func if you only want to mark @c key as being used.+CP_EXPORT cpBool cpSpaceAddPostStepCallback(cpSpace *space, cpPostStepFunc func, void *key, void *data);+++//MARK: Queries++// TODO: Queries and iterators should take a cpSpace parametery.+// TODO: They should also be abortable.++/// Nearest point query callback function type.+typedef void (*cpSpacePointQueryFunc)(cpShape *shape, cpVect point, cpFloat distance, cpVect gradient, void *data);+/// Query the space at a point and call @c func for each shape found.+CP_EXPORT void cpSpacePointQuery(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryFunc func, void *data);+/// Query the space at a point and return the nearest shape found. Returns NULL if no shapes were found.+CP_EXPORT cpShape *cpSpacePointQueryNearest(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpPointQueryInfo *out);++/// Segment query callback function type.+typedef void (*cpSpaceSegmentQueryFunc)(cpShape *shape, cpVect point, cpVect normal, cpFloat alpha, void *data);+/// Perform a directed line segment query (like a raycast) against the space calling @c func for each shape intersected.+CP_EXPORT void cpSpaceSegmentQuery(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryFunc func, void *data);+/// Perform a directed line segment query (like a raycast) against the space and return the first shape hit. Returns NULL if no shapes were hit.+CP_EXPORT cpShape *cpSpaceSegmentQueryFirst(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSegmentQueryInfo *out);++/// Rectangle Query callback function type.+typedef void (*cpSpaceBBQueryFunc)(cpShape *shape, void *data);+/// Perform a fast rectangle query on the space calling @c func for each shape found.+/// Only the shape's bounding boxes are checked for overlap, not their full shape.+CP_EXPORT void cpSpaceBBQuery(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryFunc func, void *data);++/// Shape query callback function type.+typedef void (*cpSpaceShapeQueryFunc)(cpShape *shape, cpContactPointSet *points, void *data);+/// Query a space for any shapes overlapping the given shape and call @c func for each shape found.+CP_EXPORT cpBool cpSpaceShapeQuery(cpSpace *space, cpShape *shape, cpSpaceShapeQueryFunc func, void *data);+++//MARK: Iteration++/// Space/body iterator callback function type.+typedef void (*cpSpaceBodyIteratorFunc)(cpBody *body, void *data);+/// Call @c func for each body in the space.+CP_EXPORT void cpSpaceEachBody(cpSpace *space, cpSpaceBodyIteratorFunc func, void *data);++/// Space/body iterator callback function type.+typedef void (*cpSpaceShapeIteratorFunc)(cpShape *shape, void *data);+/// Call @c func for each shape in the space.+CP_EXPORT void cpSpaceEachShape(cpSpace *space, cpSpaceShapeIteratorFunc func, void *data);++/// Space/constraint iterator callback function type.+typedef void (*cpSpaceConstraintIteratorFunc)(cpConstraint *constraint, void *data);+/// Call @c func for each shape in the space.+CP_EXPORT void cpSpaceEachConstraint(cpSpace *space, cpSpaceConstraintIteratorFunc func, void *data);+++//MARK: Indexing++/// Update the collision detection info for the static shapes in the space.+CP_EXPORT void cpSpaceReindexStatic(cpSpace *space);+/// Update the collision detection data for a specific shape in the space.+CP_EXPORT void cpSpaceReindexShape(cpSpace *space, cpShape *shape);+/// Update the collision detection data for all shapes attached to a body.+CP_EXPORT void cpSpaceReindexShapesForBody(cpSpace *space, cpBody *body);++/// Switch the space to use a spatial has as it's spatial index.+CP_EXPORT void cpSpaceUseSpatialHash(cpSpace *space, cpFloat dim, int count);+++//MARK: Time Stepping++/// Step the space forward in time by @c dt.+CP_EXPORT void cpSpaceStep(cpSpace *space, cpFloat dt);+++//MARK: Debug API++#ifndef CP_SPACE_DISABLE_DEBUG_API++/// Color type to use with the space debug drawing API.+typedef struct cpSpaceDebugColor {+ float r, g, b, a;+} cpSpaceDebugColor;++/// Callback type for a function that draws a filled, stroked circle.+typedef void (*cpSpaceDebugDrawCircleImpl)(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data);+/// Callback type for a function that draws a line segment.+typedef void (*cpSpaceDebugDrawSegmentImpl)(cpVect a, cpVect b, cpSpaceDebugColor color, cpDataPointer data);+/// Callback type for a function that draws a thick line segment.+typedef void (*cpSpaceDebugDrawFatSegmentImpl)(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data);+/// Callback type for a function that draws a convex polygon.+typedef void (*cpSpaceDebugDrawPolygonImpl)(int count, const cpVect *verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data);+/// Callback type for a function that draws a dot.+typedef void (*cpSpaceDebugDrawDotImpl)(cpFloat size, cpVect pos, cpSpaceDebugColor color, cpDataPointer data);+/// Callback type for a function that returns a color for a given shape. This gives you an opportunity to color shapes based on how they are used in your engine.+typedef cpSpaceDebugColor (*cpSpaceDebugDrawColorForShapeImpl)(cpShape *shape, cpDataPointer data);++typedef enum cpSpaceDebugDrawFlags {+ CP_SPACE_DEBUG_DRAW_SHAPES = 1<<0,+ CP_SPACE_DEBUG_DRAW_CONSTRAINTS = 1<<1,+ CP_SPACE_DEBUG_DRAW_COLLISION_POINTS = 1<<2,+} cpSpaceDebugDrawFlags;++/// Struct used with cpSpaceDebugDraw() containing drawing callbacks and other drawing settings.+typedef struct cpSpaceDebugDrawOptions {+ /// Function that will be invoked to draw circles.+ cpSpaceDebugDrawCircleImpl drawCircle;+ /// Function that will be invoked to draw line segments.+ cpSpaceDebugDrawSegmentImpl drawSegment;+ /// Function that will be invoked to draw thick line segments.+ cpSpaceDebugDrawFatSegmentImpl drawFatSegment;+ /// Function that will be invoked to draw convex polygons.+ cpSpaceDebugDrawPolygonImpl drawPolygon;+ /// Function that will be invoked to draw dots.+ cpSpaceDebugDrawDotImpl drawDot;+ + /// Flags that request which things to draw (collision shapes, constraints, contact points).+ cpSpaceDebugDrawFlags flags;+ /// Outline color passed to the drawing function.+ cpSpaceDebugColor shapeOutlineColor;+ /// Function that decides what fill color to draw shapes using.+ cpSpaceDebugDrawColorForShapeImpl colorForShape;+ /// Color passed to drawing functions for constraints.+ cpSpaceDebugColor constraintColor;+ /// Color passed to drawing functions for collision points.+ cpSpaceDebugColor collisionPointColor;+ + /// User defined context pointer passed to all of the callback functions as the 'data' argument.+ cpDataPointer data;+} cpSpaceDebugDrawOptions;++/// Debug draw the current state of the space using the supplied drawing options.+CP_EXPORT void cpSpaceDebugDraw(cpSpace *space, cpSpaceDebugDrawOptions *options);++#endif++/// @}
+ Chipmunk2D-7.0.2/include/chipmunk/cpSpatialIndex.h view
@@ -0,0 +1,227 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++/**+ @defgroup cpSpatialIndex cpSpatialIndex+ + Spatial indexes are data structures that are used to accelerate collision detection+ and spatial queries. Chipmunk provides a number of spatial index algorithms to pick from+ and they are programmed in a generic way so that you can use them for holding more than+ just cpShape structs.+ + It works by using @c void pointers to the objects you add and using a callback to ask your code+ for bounding boxes when it needs them. Several types of queries can be performed an index as well+ as reindexing and full collision information. All communication to the spatial indexes is performed+ through callback functions.+ + Spatial indexes should be treated as opaque structs.+ This meanns you shouldn't be reading any of the struct fields.+ @{+*/++//MARK: Spatial Index++/// Spatial index bounding box callback function type.+/// The spatial index calls this function and passes you a pointer to an object you added+/// when it needs to get the bounding box associated with that object.+typedef cpBB (*cpSpatialIndexBBFunc)(void *obj);+/// Spatial index/object iterator callback function type.+typedef void (*cpSpatialIndexIteratorFunc)(void *obj, void *data);+/// Spatial query callback function type.+typedef cpCollisionID (*cpSpatialIndexQueryFunc)(void *obj1, void *obj2, cpCollisionID id, void *data);+/// Spatial segment query callback function type.+typedef cpFloat (*cpSpatialIndexSegmentQueryFunc)(void *obj1, void *obj2, void *data);+++typedef struct cpSpatialIndexClass cpSpatialIndexClass;+typedef struct cpSpatialIndex cpSpatialIndex;++/// @private+struct cpSpatialIndex {+ cpSpatialIndexClass *klass;+ + cpSpatialIndexBBFunc bbfunc;+ + cpSpatialIndex *staticIndex, *dynamicIndex;+};+++//MARK: Spatial Hash++typedef struct cpSpaceHash cpSpaceHash;++/// Allocate a spatial hash.+CP_EXPORT cpSpaceHash* cpSpaceHashAlloc(void);+/// Initialize a spatial hash. +CP_EXPORT cpSpatialIndex* cpSpaceHashInit(cpSpaceHash *hash, cpFloat celldim, int numcells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);+/// Allocate and initialize a spatial hash.+CP_EXPORT cpSpatialIndex* cpSpaceHashNew(cpFloat celldim, int cells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);++/// Change the cell dimensions and table size of the spatial hash to tune it.+/// The cell dimensions should roughly match the average size of your objects+/// and the table size should be ~10 larger than the number of objects inserted.+/// Some trial and error is required to find the optimum numbers for efficiency.+CP_EXPORT void cpSpaceHashResize(cpSpaceHash *hash, cpFloat celldim, int numcells);++//MARK: AABB Tree++typedef struct cpBBTree cpBBTree;++/// Allocate a bounding box tree.+CP_EXPORT cpBBTree* cpBBTreeAlloc(void);+/// Initialize a bounding box tree.+CP_EXPORT cpSpatialIndex* cpBBTreeInit(cpBBTree *tree, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);+/// Allocate and initialize a bounding box tree.+CP_EXPORT cpSpatialIndex* cpBBTreeNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);++/// Perform a static top down optimization of the tree.+CP_EXPORT void cpBBTreeOptimize(cpSpatialIndex *index);++/// Bounding box tree velocity callback function.+/// This function should return an estimate for the object's velocity.+typedef cpVect (*cpBBTreeVelocityFunc)(void *obj);+/// Set the velocity function for the bounding box tree to enable temporal coherence.+CP_EXPORT void cpBBTreeSetVelocityFunc(cpSpatialIndex *index, cpBBTreeVelocityFunc func);++//MARK: Single Axis Sweep++typedef struct cpSweep1D cpSweep1D;++/// Allocate a 1D sort and sweep broadphase.+CP_EXPORT cpSweep1D* cpSweep1DAlloc(void);+/// Initialize a 1D sort and sweep broadphase.+CP_EXPORT cpSpatialIndex* cpSweep1DInit(cpSweep1D *sweep, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);+/// Allocate and initialize a 1D sort and sweep broadphase.+CP_EXPORT cpSpatialIndex* cpSweep1DNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);++//MARK: Spatial Index Implementation++typedef void (*cpSpatialIndexDestroyImpl)(cpSpatialIndex *index);++typedef int (*cpSpatialIndexCountImpl)(cpSpatialIndex *index);+typedef void (*cpSpatialIndexEachImpl)(cpSpatialIndex *index, cpSpatialIndexIteratorFunc func, void *data);++typedef cpBool (*cpSpatialIndexContainsImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid);+typedef void (*cpSpatialIndexInsertImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid);+typedef void (*cpSpatialIndexRemoveImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid);++typedef void (*cpSpatialIndexReindexImpl)(cpSpatialIndex *index);+typedef void (*cpSpatialIndexReindexObjectImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid);+typedef void (*cpSpatialIndexReindexQueryImpl)(cpSpatialIndex *index, cpSpatialIndexQueryFunc func, void *data);++typedef void (*cpSpatialIndexQueryImpl)(cpSpatialIndex *index, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data);+typedef void (*cpSpatialIndexSegmentQueryImpl)(cpSpatialIndex *index, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data);++struct cpSpatialIndexClass {+ cpSpatialIndexDestroyImpl destroy;+ + cpSpatialIndexCountImpl count;+ cpSpatialIndexEachImpl each;+ + cpSpatialIndexContainsImpl contains;+ cpSpatialIndexInsertImpl insert;+ cpSpatialIndexRemoveImpl remove;+ + cpSpatialIndexReindexImpl reindex;+ cpSpatialIndexReindexObjectImpl reindexObject;+ cpSpatialIndexReindexQueryImpl reindexQuery;+ + cpSpatialIndexQueryImpl query;+ cpSpatialIndexSegmentQueryImpl segmentQuery;+};++/// Destroy and free a spatial index.+CP_EXPORT void cpSpatialIndexFree(cpSpatialIndex *index);+/// Collide the objects in @c dynamicIndex against the objects in @c staticIndex using the query callback function.+CP_EXPORT void cpSpatialIndexCollideStatic(cpSpatialIndex *dynamicIndex, cpSpatialIndex *staticIndex, cpSpatialIndexQueryFunc func, void *data);++/// Destroy a spatial index.+static inline void cpSpatialIndexDestroy(cpSpatialIndex *index)+{+ if(index->klass) index->klass->destroy(index);+}++/// Get the number of objects in the spatial index.+static inline int cpSpatialIndexCount(cpSpatialIndex *index)+{+ return index->klass->count(index);+}++/// Iterate the objects in the spatial index. @c func will be called once for each object.+static inline void cpSpatialIndexEach(cpSpatialIndex *index, cpSpatialIndexIteratorFunc func, void *data)+{+ index->klass->each(index, func, data);+}++/// Returns true if the spatial index contains the given object.+/// Most spatial indexes use hashed storage, so you must provide a hash value too.+static inline cpBool cpSpatialIndexContains(cpSpatialIndex *index, void *obj, cpHashValue hashid)+{+ return index->klass->contains(index, obj, hashid);+}++/// Add an object to a spatial index.+/// Most spatial indexes use hashed storage, so you must provide a hash value too.+static inline void cpSpatialIndexInsert(cpSpatialIndex *index, void *obj, cpHashValue hashid)+{+ index->klass->insert(index, obj, hashid);+}++/// Remove an object from a spatial index.+/// Most spatial indexes use hashed storage, so you must provide a hash value too.+static inline void cpSpatialIndexRemove(cpSpatialIndex *index, void *obj, cpHashValue hashid)+{+ index->klass->remove(index, obj, hashid);+}++/// Perform a full reindex of a spatial index.+static inline void cpSpatialIndexReindex(cpSpatialIndex *index)+{+ index->klass->reindex(index);+}++/// Reindex a single object in the spatial index.+static inline void cpSpatialIndexReindexObject(cpSpatialIndex *index, void *obj, cpHashValue hashid)+{+ index->klass->reindexObject(index, obj, hashid);+}++/// Perform a rectangle query against the spatial index, calling @c func for each potential match.+static inline void cpSpatialIndexQuery(cpSpatialIndex *index, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data)+{+ index->klass->query(index, obj, bb, func, data);+}++/// Perform a segment query against the spatial index, calling @c func for each potential match.+static inline void cpSpatialIndexSegmentQuery(cpSpatialIndex *index, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data)+{+ index->klass->segmentQuery(index, obj, a, b, t_exit, func, data);+}++/// Simultaneously reindex and find all colliding objects.+/// @c func will be called once for each potentially overlapping pair of objects found.+/// If the spatial index was initialized with a static index, it will collide it's objects against that as well.+static inline void cpSpatialIndexReindexQuery(cpSpatialIndex *index, cpSpatialIndexQueryFunc func, void *data)+{+ index->klass->reindexQuery(index, func, data);+}++///@}
+ Chipmunk2D-7.0.2/include/chipmunk/cpTransform.h view
@@ -0,0 +1,198 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#ifndef CHIPMUNK_TRANSFORM_H+#define CHIPMUNK_TRANSFORM_H++#include "chipmunk_types.h"+#include "cpVect.h"+#include "cpBB.h"++/// Identity transform matrix.+static const cpTransform cpTransformIdentity = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};++/// Construct a new transform matrix.+/// (a, b) is the x basis vector.+/// (c, d) is the y basis vector.+/// (tx, ty) is the translation.+static inline cpTransform+cpTransformNew(cpFloat a, cpFloat b, cpFloat c, cpFloat d, cpFloat tx, cpFloat ty)+{+ cpTransform t = {a, b, c, d, tx, ty};+ return t;+}++/// Construct a new transform matrix in transposed order.+static inline cpTransform+cpTransformNewTranspose(cpFloat a, cpFloat c, cpFloat tx, cpFloat b, cpFloat d, cpFloat ty)+{+ cpTransform t = {a, b, c, d, tx, ty};+ return t;+}++/// Get the inverse of a transform matrix.+static inline cpTransform+cpTransformInverse(cpTransform t)+{+ cpFloat inv_det = 1.0/(t.a*t.d - t.c*t.b);+ return cpTransformNewTranspose(+ t.d*inv_det, -t.c*inv_det, (t.c*t.ty - t.tx*t.d)*inv_det,+ -t.b*inv_det, t.a*inv_det, (t.tx*t.b - t.a*t.ty)*inv_det+ );+}++/// Multiply two transformation matrices.+static inline cpTransform+cpTransformMult(cpTransform t1, cpTransform t2)+{+ return cpTransformNewTranspose(+ t1.a*t2.a + t1.c*t2.b, t1.a*t2.c + t1.c*t2.d, t1.a*t2.tx + t1.c*t2.ty + t1.tx,+ t1.b*t2.a + t1.d*t2.b, t1.b*t2.c + t1.d*t2.d, t1.b*t2.tx + t1.d*t2.ty + t1.ty+ );+}++/// Transform an absolute point. (i.e. a vertex)+static inline cpVect+cpTransformPoint(cpTransform t, cpVect p)+{+ return cpv(t.a*p.x + t.c*p.y + t.tx, t.b*p.x + t.d*p.y + t.ty);+}++/// Transform a vector (i.e. a normal)+static inline cpVect+cpTransformVect(cpTransform t, cpVect v)+{+ return cpv(t.a*v.x + t.c*v.y, t.b*v.x + t.d*v.y);+}++/// Transform a cpBB.+static inline cpBB+cpTransformbBB(cpTransform t, cpBB bb)+{+ cpVect center = cpBBCenter(bb);+ cpFloat hw = (bb.r - bb.l)*0.5;+ cpFloat hh = (bb.t - bb.b)*0.5;+ + cpFloat a = t.a*hw, b = t.c*hh, d = t.b*hw, e = t.d*hh;+ cpFloat hw_max = cpfmax(cpfabs(a + b), cpfabs(a - b));+ cpFloat hh_max = cpfmax(cpfabs(d + e), cpfabs(d - e));+ return cpBBNewForExtents(cpTransformPoint(t, center), hw_max, hh_max);+}++/// Create a transation matrix.+static inline cpTransform+cpTransformTranslate(cpVect translate)+{+ return cpTransformNewTranspose(+ 1.0, 0.0, translate.x,+ 0.0, 1.0, translate.y+ );+}++/// Create a scale matrix.+static inline cpTransform+cpTransformScale(cpFloat scaleX, cpFloat scaleY)+{+ return cpTransformNewTranspose(+ scaleX, 0.0, 0.0,+ 0.0, scaleY, 0.0+ );+}++/// Create a rotation matrix.+static inline cpTransform+cpTransformRotate(cpFloat radians)+{+ cpVect rot = cpvforangle(radians);+ return cpTransformNewTranspose(+ rot.x, -rot.y, 0.0,+ rot.y, rot.x, 0.0+ );+}++/// Create a rigid transformation matrix. (transation + rotation)+static inline cpTransform+cpTransformRigid(cpVect translate, cpFloat radians)+{+ cpVect rot = cpvforangle(radians);+ return cpTransformNewTranspose(+ rot.x, -rot.y, translate.x,+ rot.y, rot.x, translate.y+ );+}++/// Fast inverse of a rigid transformation matrix.+static inline cpTransform+cpTransformRigidInverse(cpTransform t)+{+ return cpTransformNewTranspose(+ t.d, -t.c, (t.c*t.ty - t.tx*t.d),+ -t.b, t.a, (t.tx*t.b - t.a*t.ty)+ );+}++//MARK: Miscellaneous (but useful) transformation matrices.+// See source for documentation...++static inline cpTransform+cpTransformWrap(cpTransform outer, cpTransform inner)+{+ return cpTransformMult(cpTransformInverse(outer), cpTransformMult(inner, outer));+}++static inline cpTransform+cpTransformWrapInverse(cpTransform outer, cpTransform inner)+{+ return cpTransformMult(outer, cpTransformMult(inner, cpTransformInverse(outer)));+}++static inline cpTransform+cpTransformOrtho(cpBB bb)+{+ return cpTransformNewTranspose(+ 2.0/(bb.r - bb.l), 0.0, -(bb.r + bb.l)/(bb.r - bb.l),+ 0.0, 2.0/(bb.t - bb.b), -(bb.t + bb.b)/(bb.t - bb.b)+ );+}++static inline cpTransform+cpTransformBoneScale(cpVect v0, cpVect v1)+{+ cpVect d = cpvsub(v1, v0); + return cpTransformNewTranspose(+ d.x, -d.y, v0.x,+ d.y, d.x, v0.y+ );+}++static inline cpTransform+cpTransformAxialScale(cpVect axis, cpVect pivot, cpFloat scale)+{+ cpFloat A = axis.x*axis.y*(scale - 1.0);+ cpFloat B = cpvdot(axis, pivot)*(1.0 - scale);+ + return cpTransformNewTranspose(+ scale*axis.x*axis.x + axis.y*axis.y, A, axis.x*B,+ A, axis.x*axis.x + scale*axis.y*axis.y, axis.y*B+ );+}++#endif
+ Chipmunk2D-7.0.2/include/chipmunk/cpVect.h view
@@ -0,0 +1,230 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#ifndef CHIPMUNK_VECT_H+#define CHIPMUNK_VECT_H++#include "chipmunk_types.h"++/// @defgroup cpVect cpVect+/// Chipmunk's 2D vector type along with a handy 2D vector math lib.+/// @{++/// Constant for the zero vector.+static const cpVect cpvzero = {0.0f,0.0f};++/// Convenience constructor for cpVect structs.+static inline cpVect cpv(const cpFloat x, const cpFloat y)+{+ cpVect v = {x, y};+ return v;+}++/// Check if two vectors are equal. (Be careful when comparing floating point numbers!)+static inline cpBool cpveql(const cpVect v1, const cpVect v2)+{+ return (v1.x == v2.x && v1.y == v2.y);+}++/// Add two vectors+static inline cpVect cpvadd(const cpVect v1, const cpVect v2)+{+ return cpv(v1.x + v2.x, v1.y + v2.y);+}++/// Subtract two vectors.+static inline cpVect cpvsub(const cpVect v1, const cpVect v2)+{+ return cpv(v1.x - v2.x, v1.y - v2.y);+}++/// Negate a vector.+static inline cpVect cpvneg(const cpVect v)+{+ return cpv(-v.x, -v.y);+}++/// Scalar multiplication.+static inline cpVect cpvmult(const cpVect v, const cpFloat s)+{+ return cpv(v.x*s, v.y*s);+}++/// Vector dot product.+static inline cpFloat cpvdot(const cpVect v1, const cpVect v2)+{+ return v1.x*v2.x + v1.y*v2.y;+}++/// 2D vector cross product analog.+/// The cross product of 2D vectors results in a 3D vector with only a z component.+/// This function returns the magnitude of the z value.+static inline cpFloat cpvcross(const cpVect v1, const cpVect v2)+{+ return v1.x*v2.y - v1.y*v2.x;+}++/// Returns a perpendicular vector. (90 degree rotation)+static inline cpVect cpvperp(const cpVect v)+{+ return cpv(-v.y, v.x);+}++/// Returns a perpendicular vector. (-90 degree rotation)+static inline cpVect cpvrperp(const cpVect v)+{+ return cpv(v.y, -v.x);+}++/// Returns the vector projection of v1 onto v2.+static inline cpVect cpvproject(const cpVect v1, const cpVect v2)+{+ return cpvmult(v2, cpvdot(v1, v2)/cpvdot(v2, v2));+}++/// Returns the unit length vector for the given angle (in radians).+static inline cpVect cpvforangle(const cpFloat a)+{+ return cpv(cpfcos(a), cpfsin(a));+}++/// Returns the angular direction v is pointing in (in radians).+static inline cpFloat cpvtoangle(const cpVect v)+{+ return cpfatan2(v.y, v.x);+}++/// Uses complex number multiplication to rotate v1 by v2. Scaling will occur if v1 is not a unit vector.+static inline cpVect cpvrotate(const cpVect v1, const cpVect v2)+{+ return cpv(v1.x*v2.x - v1.y*v2.y, v1.x*v2.y + v1.y*v2.x);+}++/// Inverse of cpvrotate().+static inline cpVect cpvunrotate(const cpVect v1, const cpVect v2)+{+ return cpv(v1.x*v2.x + v1.y*v2.y, v1.y*v2.x - v1.x*v2.y);+}++/// Returns the squared length of v. Faster than cpvlength() when you only need to compare lengths.+static inline cpFloat cpvlengthsq(const cpVect v)+{+ return cpvdot(v, v);+}++/// Returns the length of v.+static inline cpFloat cpvlength(const cpVect v)+{+ return cpfsqrt(cpvdot(v, v));+}++/// Linearly interpolate between v1 and v2.+static inline cpVect cpvlerp(const cpVect v1, const cpVect v2, const cpFloat t)+{+ return cpvadd(cpvmult(v1, 1.0f - t), cpvmult(v2, t));+}++/// Returns a normalized copy of v.+static inline cpVect cpvnormalize(const cpVect v)+{+ // Neat trick I saw somewhere to avoid div/0.+ return cpvmult(v, 1.0f/(cpvlength(v) + CPFLOAT_MIN));+}++/// Spherical linearly interpolate between v1 and v2.+static inline cpVect+cpvslerp(const cpVect v1, const cpVect v2, const cpFloat t)+{+ cpFloat dot = cpvdot(cpvnormalize(v1), cpvnormalize(v2));+ cpFloat omega = cpfacos(cpfclamp(dot, -1.0f, 1.0f));+ + if(omega < 1e-3){+ // If the angle between two vectors is very small, lerp instead to avoid precision issues.+ return cpvlerp(v1, v2, t);+ } else {+ cpFloat denom = 1.0f/cpfsin(omega);+ return cpvadd(cpvmult(v1, cpfsin((1.0f - t)*omega)*denom), cpvmult(v2, cpfsin(t*omega)*denom));+ }+}++/// Spherical linearly interpolate between v1 towards v2 by no more than angle a radians+static inline cpVect+cpvslerpconst(const cpVect v1, const cpVect v2, const cpFloat a)+{+ cpFloat dot = cpvdot(cpvnormalize(v1), cpvnormalize(v2));+ cpFloat omega = cpfacos(cpfclamp(dot, -1.0f, 1.0f));+ + return cpvslerp(v1, v2, cpfmin(a, omega)/omega);+}++/// Clamp v to length len.+static inline cpVect cpvclamp(const cpVect v, const cpFloat len)+{+ return (cpvdot(v,v) > len*len) ? cpvmult(cpvnormalize(v), len) : v;+}++/// Linearly interpolate between v1 towards v2 by distance d.+static inline cpVect cpvlerpconst(cpVect v1, cpVect v2, cpFloat d)+{+ return cpvadd(v1, cpvclamp(cpvsub(v2, v1), d));+}++/// Returns the distance between v1 and v2.+static inline cpFloat cpvdist(const cpVect v1, const cpVect v2)+{+ return cpvlength(cpvsub(v1, v2));+}++/// Returns the squared distance between v1 and v2. Faster than cpvdist() when you only need to compare distances.+static inline cpFloat cpvdistsq(const cpVect v1, const cpVect v2)+{+ return cpvlengthsq(cpvsub(v1, v2));+}++/// Returns true if the distance between v1 and v2 is less than dist.+static inline cpBool cpvnear(const cpVect v1, const cpVect v2, const cpFloat dist)+{+ return cpvdistsq(v1, v2) < dist*dist;+}++/// @}++/// @defgroup cpMat2x2 cpMat2x2+/// 2x2 matrix type used for tensors and such.+/// @{++// NUKE+static inline cpMat2x2+cpMat2x2New(cpFloat a, cpFloat b, cpFloat c, cpFloat d)+{+ cpMat2x2 m = {a, b, c, d};+ return m;+}++static inline cpVect+cpMat2x2Transform(cpMat2x2 m, cpVect v)+{+ return cpv(v.x*m.a + v.y*m.b, v.x*m.c + v.y*m.d);+}++///@}++#endif
+ Chipmunk2D-7.0.2/src/chipmunk.c view
@@ -0,0 +1,331 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include <stdio.h>+#include <string.h>+#include <stdarg.h>+#if defined(ANDROID)+# include <android/log.h>+#endif++#include "chipmunk/chipmunk_private.h"++void+cpMessage(const char *condition, const char *file, int line, int isError, int isHardError, const char *message, ...)+{+ fprintf(stderr, (isError ? "Aborting due to Chipmunk error: " : "Chipmunk warning: "));+ + va_list vargs;+ va_start(vargs, message); {+#if defined(ANDROID)+ __android_log_print( ANDROID_LOG_INFO, "Chipmunk", "%s(%d)", file, line );+ __android_log_print( ANDROID_LOG_INFO, "Chipmunk", message, vargs );+#else+ vfprintf(stderr, message, vargs);+ fprintf(stderr, "\n");+#endif+ } va_end(vargs);+ +#if defined(ANDROID)+ __android_log_print(ANDROID_LOG_INFO, "Chipmunk", "\tFailed condition: %s\n", condition);+ __android_log_print(ANDROID_LOG_INFO, "Chipmunk", "\tSource:%s:%d\n", file, line);+#else+ fprintf(stderr, "\tFailed condition: %s\n", condition);+ fprintf(stderr, "\tSource:%s:%d\n", file, line);+#endif+}++#define STR(s) #s+#define XSTR(s) STR(s)++const char *cpVersionString = XSTR(CP_VERSION_MAJOR) "." XSTR(CP_VERSION_MINOR) "." XSTR(CP_VERSION_RELEASE);++//MARK: Misc Functions++cpFloat+cpMomentForCircle(cpFloat m, cpFloat r1, cpFloat r2, cpVect offset)+{+ return m*(0.5f*(r1*r1 + r2*r2) + cpvlengthsq(offset));+}++cpFloat+cpAreaForCircle(cpFloat r1, cpFloat r2)+{+ return (cpFloat)CP_PI*cpfabs(r1*r1 - r2*r2);+}++cpFloat+cpMomentForSegment(cpFloat m, cpVect a, cpVect b, cpFloat r)+{+ cpVect offset = cpvlerp(a, b, 0.5f);+ + // This approximates the shape as a box for rounded segments, but it's quite close.+ cpFloat length = cpvdist(b, a) + 2.0f*r;+ return m*((length*length + 4.0f*r*r)/12.0f + cpvlengthsq(offset));+}++cpFloat+cpAreaForSegment(cpVect a, cpVect b, cpFloat r)+{+ return r*((cpFloat)CP_PI*r + 2.0f*cpvdist(a, b));+}++cpFloat+cpMomentForPoly(cpFloat m, const int count, const cpVect *verts, cpVect offset, cpFloat r)+{+ // TODO account for radius.+ if(count == 2) return cpMomentForSegment(m, verts[0], verts[1], 0.0f);+ + cpFloat sum1 = 0.0f;+ cpFloat sum2 = 0.0f;+ for(int i=0; i<count; i++){+ cpVect v1 = cpvadd(verts[i], offset);+ cpVect v2 = cpvadd(verts[(i+1)%count], offset);+ + cpFloat a = cpvcross(v2, v1);+ cpFloat b = cpvdot(v1, v1) + cpvdot(v1, v2) + cpvdot(v2, v2);+ + sum1 += a*b;+ sum2 += a;+ }+ + return (m*sum1)/(6.0f*sum2);+}++cpFloat+cpAreaForPoly(const int count, const cpVect *verts, cpFloat r)+{+ cpFloat area = 0.0f;+ cpFloat perimeter = 0.0f;+ for(int i=0; i<count; i++){+ cpVect v1 = verts[i];+ cpVect v2 = verts[(i+1)%count];+ + area += cpvcross(v1, v2);+ perimeter += cpvdist(v1, v2);+ }+ + return r*(CP_PI*cpfabs(r) + perimeter) + area/2.0f;+}++cpVect+cpCentroidForPoly(const int count, const cpVect *verts)+{+ cpFloat sum = 0.0f;+ cpVect vsum = cpvzero;+ + for(int i=0; i<count; i++){+ cpVect v1 = verts[i];+ cpVect v2 = verts[(i+1)%count];+ cpFloat cross = cpvcross(v1, v2);+ + sum += cross;+ vsum = cpvadd(vsum, cpvmult(cpvadd(v1, v2), cross));+ }+ + return cpvmult(vsum, 1.0f/(3.0f*sum));+}++//void+//cpRecenterPoly(const int count, cpVect *verts){+// cpVect centroid = cpCentroidForPoly(count, verts);+// +// for(int i=0; i<count; i++){+// verts[i] = cpvsub(verts[i], centroid);+// }+//}++cpFloat+cpMomentForBox(cpFloat m, cpFloat width, cpFloat height)+{+ return m*(width*width + height*height)/12.0f;+}++cpFloat+cpMomentForBox2(cpFloat m, cpBB box)+{+ cpFloat width = box.r - box.l;+ cpFloat height = box.t - box.b;+ cpVect offset = cpvmult(cpv(box.l + box.r, box.b + box.t), 0.5f);+ + // TODO: NaN when offset is 0 and m is INFINITY+ return cpMomentForBox(m, width, height) + m*cpvlengthsq(offset);+}++//MARK: Quick Hull++void+cpLoopIndexes(const cpVect *verts, int count, int *start, int *end)+{+ (*start) = (*end) = 0;+ cpVect min = verts[0];+ cpVect max = min;+ + for(int i=1; i<count; i++){+ cpVect v = verts[i];+ + if(v.x < min.x || (v.x == min.x && v.y < min.y)){+ min = v;+ (*start) = i;+ } else if(v.x > max.x || (v.x == max.x && v.y > max.y)){+ max = v;+ (*end) = i;+ }+ }+}++#define SWAP(__A__, __B__) {cpVect __TMP__ = __A__; __A__ = __B__; __B__ = __TMP__;}++static int+QHullPartition(cpVect *verts, int count, cpVect a, cpVect b, cpFloat tol)+{+ if(count == 0) return 0;+ + cpFloat max = 0;+ int pivot = 0;+ + cpVect delta = cpvsub(b, a);+ cpFloat valueTol = tol*cpvlength(delta);+ + int head = 0;+ for(int tail = count-1; head <= tail;){+ cpFloat value = cpvcross(cpvsub(verts[head], a), delta);+ if(value > valueTol){+ if(value > max){+ max = value;+ pivot = head;+ }+ + head++;+ } else {+ SWAP(verts[head], verts[tail]);+ tail--;+ }+ }+ + // move the new pivot to the front if it's not already there.+ if(pivot != 0) SWAP(verts[0], verts[pivot]);+ return head;+}++static int+QHullReduce(cpFloat tol, cpVect *verts, int count, cpVect a, cpVect pivot, cpVect b, cpVect *result)+{+ if(count < 0){+ return 0;+ } else if(count == 0) {+ result[0] = pivot;+ return 1;+ } else {+ int left_count = QHullPartition(verts, count, a, pivot, tol);+ int index = QHullReduce(tol, verts + 1, left_count - 1, a, verts[0], pivot, result);+ + result[index++] = pivot;+ + int right_count = QHullPartition(verts + left_count, count - left_count, pivot, b, tol);+ return index + QHullReduce(tol, verts + left_count + 1, right_count - 1, pivot, verts[left_count], b, result + index);+ }+}++// 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, const cpVect *verts, cpVect *result, int *first, cpFloat tol)+{+ if(verts != 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));+ }+ + // Degenerate case, all points 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;+ return QHullReduce(tol, result + 2, count - 2, a, b, a, result + 1) + 1;+}++//MARK: Alternate Block Iterators++#if defined(__has_extension)+#if __has_extension(blocks)++static void IteratorFunc(void *ptr, void (^block)(void *ptr)){block(ptr);}++void cpSpaceEachBody_b(cpSpace *space, void (^block)(cpBody *body)){+ cpSpaceEachBody(space, (cpSpaceBodyIteratorFunc)IteratorFunc, block);+}++void cpSpaceEachShape_b(cpSpace *space, void (^block)(cpShape *shape)){+ cpSpaceEachShape(space, (cpSpaceShapeIteratorFunc)IteratorFunc, block);+}++void cpSpaceEachConstraint_b(cpSpace *space, void (^block)(cpConstraint *constraint)){+ cpSpaceEachConstraint(space, (cpSpaceConstraintIteratorFunc)IteratorFunc, block);+}++static void BodyIteratorFunc(cpBody *body, void *ptr, void (^block)(void *ptr)){block(ptr);}++void cpBodyEachShape_b(cpBody *body, void (^block)(cpShape *shape)){+ cpBodyEachShape(body, (cpBodyShapeIteratorFunc)BodyIteratorFunc, block);+}++void cpBodyEachConstraint_b(cpBody *body, void (^block)(cpConstraint *constraint)){+ cpBodyEachConstraint(body, (cpBodyConstraintIteratorFunc)BodyIteratorFunc, block);+}++void cpBodyEachArbiter_b(cpBody *body, void (^block)(cpArbiter *arbiter)){+ cpBodyEachArbiter(body, (cpBodyArbiterIteratorFunc)BodyIteratorFunc, block);+}++static void PointQueryIteratorFunc(cpShape *shape, cpVect p, cpFloat d, cpVect g, cpSpacePointQueryBlock block){block(shape, p, d, g);}+void cpSpacePointQuery_b(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryBlock block){+ cpSpacePointQuery(space, point, maxDistance, filter, (cpSpacePointQueryFunc)PointQueryIteratorFunc, block);+}++static void SegmentQueryIteratorFunc(cpShape *shape, cpVect p, cpVect n, cpFloat t, cpSpaceSegmentQueryBlock block){block(shape, p, n, t);}+void cpSpaceSegmentQuery_b(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryBlock block){+ cpSpaceSegmentQuery(space, start, end, radius, filter, (cpSpaceSegmentQueryFunc)SegmentQueryIteratorFunc, block);+}++void cpSpaceBBQuery_b(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryBlock block){+ cpSpaceBBQuery(space, bb, filter, (cpSpaceBBQueryFunc)IteratorFunc, block);+}++static void ShapeQueryIteratorFunc(cpShape *shape, cpContactPointSet *points, cpSpaceShapeQueryBlock block){block(shape, points);}+cpBool cpSpaceShapeQuery_b(cpSpace *space, cpShape *shape, cpSpaceShapeQueryBlock block){+ return cpSpaceShapeQuery(space, shape, (cpSpaceShapeQueryFunc)ShapeQueryIteratorFunc, block);+}++#endif+#endif++#include "chipmunk/chipmunk_ffi.h"
+ Chipmunk2D-7.0.2/src/cpArbiter.c view
@@ -0,0 +1,496 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++// TODO: make this generic so I can reuse it for constraints also.+static inline void+unthreadHelper(cpArbiter *arb, cpBody *body)+{+ struct cpArbiterThread *thread = cpArbiterThreadForBody(arb, body);+ cpArbiter *prev = thread->prev;+ cpArbiter *next = thread->next;+ + if(prev){+ cpArbiterThreadForBody(prev, body)->next = next;+ } else if(body->arbiterList == arb) {+ // IFF prev is NULL and body->arbiterList == arb, is arb at the head of the list.+ // This function may be called for an arbiter that was never in a list.+ // In that case, we need to protect it from wiping out the body->arbiterList pointer.+ body->arbiterList = next;+ }+ + if(next) cpArbiterThreadForBody(next, body)->prev = prev;+ + thread->prev = NULL;+ thread->next = NULL;+}++void+cpArbiterUnthread(cpArbiter *arb)+{+ unthreadHelper(arb, arb->body_a);+ unthreadHelper(arb, arb->body_b);+}++cpBool cpArbiterIsFirstContact(const cpArbiter *arb)+{+ return arb->state == CP_ARBITER_STATE_FIRST_COLLISION;+}++cpBool cpArbiterIsRemoval(const cpArbiter *arb)+{+ return arb->state == CP_ARBITER_STATE_INVALIDATED;+}++int cpArbiterGetCount(const cpArbiter *arb)+{+ // Return 0 contacts if we are in a separate callback.+ return (arb->state < CP_ARBITER_STATE_CACHED ? arb->count : 0);+}++cpVect+cpArbiterGetNormal(const cpArbiter *arb)+{+ return cpvmult(arb->n, arb->swapped ? -1.0f : 1.0);+}++cpVect+cpArbiterGetPointA(const cpArbiter *arb, int i)+{+ cpAssertHard(0 <= i && i < cpArbiterGetCount(arb), "Index error: The specified contact index is invalid for this arbiter");+ return cpvadd(arb->body_a->p, arb->contacts[i].r1);+}++cpVect+cpArbiterGetPointB(const cpArbiter *arb, int i)+{+ cpAssertHard(0 <= i && i < cpArbiterGetCount(arb), "Index error: The specified contact index is invalid for this arbiter");+ return cpvadd(arb->body_b->p, arb->contacts[i].r2);+}++cpFloat+cpArbiterGetDepth(const cpArbiter *arb, int i)+{+ cpAssertHard(0 <= i && i < cpArbiterGetCount(arb), "Index error: The specified contact index is invalid for this arbiter");+ + struct cpContact *con = &arb->contacts[i];+ return cpvdot(cpvadd(cpvsub(con->r2, con->r1), cpvsub(arb->body_b->p, arb->body_a->p)), arb->n);+}++cpContactPointSet+cpArbiterGetContactPointSet(const cpArbiter *arb)+{+ cpContactPointSet set;+ set.count = cpArbiterGetCount(arb);+ + cpBool swapped = arb->swapped;+ cpVect n = arb->n;+ set.normal = (swapped ? cpvneg(n) : n);+ + for(int i=0; i<set.count; i++){+ // Contact points are relative to body CoGs;+ cpVect p1 = cpvadd(arb->body_a->p, arb->contacts[i].r1);+ cpVect p2 = cpvadd(arb->body_b->p, arb->contacts[i].r2);+ + set.points[i].pointA = (swapped ? p2 : p1);+ set.points[i].pointB = (swapped ? p1 : p2);+ set.points[i].distance = cpvdot(cpvsub(p2, p1), n);+ }+ + return set;+}++void+cpArbiterSetContactPointSet(cpArbiter *arb, cpContactPointSet *set)+{+ int count = set->count;+ cpAssertHard(count == arb->count, "The number of contact points cannot be changed.");+ + cpBool swapped = arb->swapped;+ arb->n = (swapped ? cpvneg(set->normal) : set->normal);+ + for(int i=0; i<count; i++){+ // Convert back to CoG relative offsets.+ cpVect p1 = set->points[i].pointA;+ cpVect p2 = set->points[i].pointB;+ + arb->contacts[i].r1 = cpvsub(swapped ? p2 : p1, arb->body_a->p);+ arb->contacts[i].r2 = cpvsub(swapped ? p1 : p2, arb->body_b->p);+ }+}++cpVect+cpArbiterTotalImpulse(const cpArbiter *arb)+{+ struct cpContact *contacts = arb->contacts;+ cpVect n = arb->n;+ cpVect sum = cpvzero;+ + for(int i=0, count=cpArbiterGetCount(arb); i<count; i++){+ struct cpContact *con = &contacts[i];+ sum = cpvadd(sum, cpvrotate(n, cpv(con->jnAcc, con->jtAcc)));+ }+ + return (arb->swapped ? sum : cpvneg(sum));+ return cpvzero;+}++cpFloat+cpArbiterTotalKE(const cpArbiter *arb)+{+ cpFloat eCoef = (1 - arb->e)/(1 + arb->e);+ cpFloat sum = 0.0;+ + struct cpContact *contacts = arb->contacts;+ for(int i=0, count=cpArbiterGetCount(arb); i<count; i++){+ struct cpContact *con = &contacts[i];+ cpFloat jnAcc = con->jnAcc;+ cpFloat jtAcc = con->jtAcc;+ + sum += eCoef*jnAcc*jnAcc/con->nMass + jtAcc*jtAcc/con->tMass;+ }+ + return sum;+}++cpBool+cpArbiterIgnore(cpArbiter *arb)+{+ arb->state = CP_ARBITER_STATE_IGNORE;+ return cpFalse;+}++cpFloat+cpArbiterGetRestitution(const cpArbiter *arb)+{+ return arb->e;+}++void+cpArbiterSetRestitution(cpArbiter *arb, cpFloat restitution)+{+ arb->e = restitution;+}++cpFloat+cpArbiterGetFriction(const cpArbiter *arb)+{+ return arb->u;+}++void+cpArbiterSetFriction(cpArbiter *arb, cpFloat friction)+{+ arb->u = friction;+}++cpVect+cpArbiterGetSurfaceVelocity(cpArbiter *arb)+{+ return cpvmult(arb->surface_vr, arb->swapped ? -1.0f : 1.0);+}++void+cpArbiterSetSurfaceVelocity(cpArbiter *arb, cpVect vr)+{+ arb->surface_vr = cpvmult(vr, arb->swapped ? -1.0f : 1.0);+}++cpDataPointer+cpArbiterGetUserData(const cpArbiter *arb)+{+ return arb->data;+}++void+cpArbiterSetUserData(cpArbiter *arb, cpDataPointer userData)+{+ arb->data = userData;+}++void+cpArbiterGetShapes(const cpArbiter *arb, cpShape **a, cpShape **b)+{+ if(arb->swapped){+ (*a) = (cpShape *)arb->b, (*b) = (cpShape *)arb->a;+ } else {+ (*a) = (cpShape *)arb->a, (*b) = (cpShape *)arb->b;+ }+}++void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody **b)+{+ CP_ARBITER_GET_SHAPES(arb, shape_a, shape_b);+ (*a) = shape_a->body;+ (*b) = shape_b->body;+}++cpBool+cpArbiterCallWildcardBeginA(cpArbiter *arb, cpSpace *space)+{+ cpCollisionHandler *handler = arb->handlerA;+ return handler->beginFunc(arb, space, handler->userData);+}++cpBool+cpArbiterCallWildcardBeginB(cpArbiter *arb, cpSpace *space)+{+ cpCollisionHandler *handler = arb->handlerB;+ arb->swapped = !arb->swapped;+ cpBool retval = handler->beginFunc(arb, space, handler->userData);+ arb->swapped = !arb->swapped;+ return retval;+}++cpBool+cpArbiterCallWildcardPreSolveA(cpArbiter *arb, cpSpace *space)+{+ cpCollisionHandler *handler = arb->handlerA;+ return handler->preSolveFunc(arb, space, handler->userData);+}++cpBool+cpArbiterCallWildcardPreSolveB(cpArbiter *arb, cpSpace *space)+{+ cpCollisionHandler *handler = arb->handlerB;+ arb->swapped = !arb->swapped;+ cpBool retval = handler->preSolveFunc(arb, space, handler->userData);+ arb->swapped = !arb->swapped;+ return retval;+}++void+cpArbiterCallWildcardPostSolveA(cpArbiter *arb, cpSpace *space)+{+ cpCollisionHandler *handler = arb->handlerA;+ handler->postSolveFunc(arb, space, handler->userData);+}++void+cpArbiterCallWildcardPostSolveB(cpArbiter *arb, cpSpace *space)+{+ cpCollisionHandler *handler = arb->handlerB;+ arb->swapped = !arb->swapped;+ handler->postSolveFunc(arb, space, handler->userData);+ arb->swapped = !arb->swapped;+}++void+cpArbiterCallWildcardSeparateA(cpArbiter *arb, cpSpace *space)+{+ cpCollisionHandler *handler = arb->handlerA;+ handler->separateFunc(arb, space, handler->userData);+}++void+cpArbiterCallWildcardSeparateB(cpArbiter *arb, cpSpace *space)+{+ cpCollisionHandler *handler = arb->handlerB;+ arb->swapped = !arb->swapped;+ handler->separateFunc(arb, space, handler->userData);+ arb->swapped = !arb->swapped;+}++cpArbiter*+cpArbiterInit(cpArbiter *arb, cpShape *a, cpShape *b)+{+ arb->handler = NULL;+ arb->swapped = cpFalse;+ + arb->handler = NULL;+ arb->handlerA = NULL;+ arb->handlerB = NULL;+ + arb->e = 0.0f;+ arb->u = 0.0f;+ arb->surface_vr = cpvzero;+ + arb->count = 0;+ arb->contacts = NULL;+ + arb->a = a; arb->body_a = a->body;+ arb->b = b; arb->body_b = b->body;+ + arb->thread_a.next = NULL;+ arb->thread_b.next = NULL;+ arb->thread_a.prev = NULL;+ arb->thread_b.prev = NULL;+ + arb->stamp = 0;+ arb->state = CP_ARBITER_STATE_FIRST_COLLISION;+ + arb->data = NULL;+ + return arb;+}++static inline cpCollisionHandler *+cpSpaceLookupHandler(cpSpace *space, cpCollisionType a, cpCollisionType b, cpCollisionHandler *defaultValue)+{+ cpCollisionType types[] = {a, b};+ cpCollisionHandler *handler = (cpCollisionHandler *)cpHashSetFind(space->collisionHandlers, CP_HASH_PAIR(a, b), types);+ return (handler ? handler : defaultValue);+}++void+cpArbiterUpdate(cpArbiter *arb, struct cpCollisionInfo *info, cpSpace *space)+{+ const cpShape *a = info->a, *b = info->b;+ + // For collisions between two similar primitive types, the order could have been swapped since the last frame.+ arb->a = a; arb->body_a = a->body;+ arb->b = b; arb->body_b = b->body;+ + // Iterate over the possible pairs to look for hash value matches.+ for(int i=0; i<info->count; i++){+ struct cpContact *con = &info->arr[i];+ + // r1 and r2 store absolute offsets at init time.+ // Need to convert them to relative offsets.+ con->r1 = cpvsub(con->r1, a->body->p);+ con->r2 = cpvsub(con->r2, b->body->p);+ + // Cached impulses are not zeroed at init time.+ con->jnAcc = con->jtAcc = 0.0f;+ + for(int j=0; j<arb->count; j++){+ struct cpContact *old = &arb->contacts[j];+ + // This could trigger false positives, but is fairly unlikely nor serious if it does.+ if(con->hash == old->hash){+ // Copy the persistant contact information.+ con->jnAcc = old->jnAcc;+ con->jtAcc = old->jtAcc;+ }+ }+ }+ + arb->contacts = info->arr;+ arb->count = info->count;+ arb->n = info->n;+ + arb->e = a->e * b->e;+ arb->u = a->u * b->u;+ + cpVect surface_vr = cpvsub(b->surfaceV, a->surfaceV);+ arb->surface_vr = cpvsub(surface_vr, cpvmult(info->n, cpvdot(surface_vr, info->n)));+ + cpCollisionType typeA = info->a->type, typeB = info->b->type;+ cpCollisionHandler *defaultHandler = &space->defaultHandler;+ cpCollisionHandler *handler = arb->handler = cpSpaceLookupHandler(space, typeA, typeB, defaultHandler);+ + // Check if the types match, but don't swap for a default handler which use the wildcard for type A.+ cpBool swapped = arb->swapped = (typeA != handler->typeA && handler->typeA != CP_WILDCARD_COLLISION_TYPE);+ + if(handler != defaultHandler || space->usesWildcards){+ // The order of the main handler swaps the wildcard handlers too. Uffda.+ arb->handlerA = cpSpaceLookupHandler(space, (swapped ? typeB : typeA), CP_WILDCARD_COLLISION_TYPE, &cpCollisionHandlerDoNothing);+ arb->handlerB = cpSpaceLookupHandler(space, (swapped ? typeA : typeB), CP_WILDCARD_COLLISION_TYPE, &cpCollisionHandlerDoNothing);+ }+ + // mark it as new if it's been cached+ if(arb->state == CP_ARBITER_STATE_CACHED) arb->state = CP_ARBITER_STATE_FIRST_COLLISION;+}++void+cpArbiterPreStep(cpArbiter *arb, cpFloat dt, cpFloat slop, cpFloat bias)+{+ cpBody *a = arb->body_a;+ cpBody *b = arb->body_b;+ cpVect n = arb->n;+ cpVect body_delta = cpvsub(b->p, a->p);+ + for(int i=0; i<arb->count; i++){+ struct cpContact *con = &arb->contacts[i];+ + // Calculate the mass normal and mass tangent.+ con->nMass = 1.0f/k_scalar(a, b, con->r1, con->r2, n);+ con->tMass = 1.0f/k_scalar(a, b, con->r1, con->r2, cpvperp(n));+ + // Calculate the target bias velocity.+ cpFloat dist = cpvdot(cpvadd(cpvsub(con->r2, con->r1), body_delta), n);+ con->bias = -bias*cpfmin(0.0f, dist + slop)/dt;+ con->jBias = 0.0f;+ + // Calculate the target bounce velocity.+ con->bounce = normal_relative_velocity(a, b, con->r1, con->r2, n)*arb->e;+ }+}++void+cpArbiterApplyCachedImpulse(cpArbiter *arb, cpFloat dt_coef)+{+ if(cpArbiterIsFirstContact(arb)) return;+ + cpBody *a = arb->body_a;+ cpBody *b = arb->body_b;+ cpVect n = arb->n;+ + for(int i=0; i<arb->count; i++){+ struct cpContact *con = &arb->contacts[i];+ cpVect j = cpvrotate(n, cpv(con->jnAcc, con->jtAcc));+ apply_impulses(a, b, con->r1, con->r2, cpvmult(j, dt_coef));+ }+}++// TODO: is it worth splitting velocity/position correction?++void+cpArbiterApplyImpulse(cpArbiter *arb)+{+ cpBody *a = arb->body_a;+ cpBody *b = arb->body_b;+ cpVect n = arb->n;+ cpVect surface_vr = arb->surface_vr;+ cpFloat friction = arb->u;++ for(int i=0; i<arb->count; i++){+ struct cpContact *con = &arb->contacts[i];+ cpFloat nMass = con->nMass;+ cpVect r1 = con->r1;+ cpVect r2 = con->r2;+ + cpVect vb1 = cpvadd(a->v_bias, cpvmult(cpvperp(r1), a->w_bias));+ cpVect vb2 = cpvadd(b->v_bias, cpvmult(cpvperp(r2), b->w_bias));+ cpVect vr = cpvadd(relative_velocity(a, b, r1, r2), surface_vr);+ + cpFloat vbn = cpvdot(cpvsub(vb2, vb1), n);+ cpFloat vrn = cpvdot(vr, n);+ cpFloat vrt = cpvdot(vr, cpvperp(n));+ + cpFloat jbn = (con->bias - vbn)*nMass;+ cpFloat jbnOld = con->jBias;+ con->jBias = cpfmax(jbnOld + jbn, 0.0f);+ + cpFloat jn = -(con->bounce + vrn)*nMass;+ cpFloat jnOld = con->jnAcc;+ con->jnAcc = cpfmax(jnOld + jn, 0.0f);+ + cpFloat jtMax = friction*con->jnAcc;+ cpFloat jt = -vrt*con->tMass;+ cpFloat jtOld = con->jtAcc;+ con->jtAcc = cpfclamp(jtOld + jt, -jtMax, jtMax);+ + apply_bias_impulses(a, b, r1, r2, cpvmult(n, con->jBias - jbnOld));+ apply_impulses(a, b, r1, r2, cpvrotate(n, cpv(con->jnAcc - jnOld, con->jtAcc - jtOld)));+ }+}
+ Chipmunk2D-7.0.2/src/cpArray.c view
@@ -0,0 +1,101 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include <string.h>++#include "chipmunk/chipmunk_private.h"+++cpArray *+cpArrayNew(int size)+{+ cpArray *arr = (cpArray *)cpcalloc(1, sizeof(cpArray));+ + arr->num = 0;+ arr->max = (size ? size : 4);+ arr->arr = (void **)cpcalloc(arr->max, sizeof(void*));+ + return arr;+}++void+cpArrayFree(cpArray *arr)+{+ if(arr){+ cpfree(arr->arr);+ arr->arr = NULL;+ + cpfree(arr);+ }+}++void+cpArrayPush(cpArray *arr, void *object)+{+ if(arr->num == arr->max){+ arr->max = 3*(arr->max + 1)/2;+ arr->arr = (void **)cprealloc(arr->arr, arr->max*sizeof(void*));+ }+ + arr->arr[arr->num] = object;+ arr->num++;+}++void *+cpArrayPop(cpArray *arr)+{+ arr->num--;+ + void *value = arr->arr[arr->num];+ arr->arr[arr->num] = NULL;+ + return value;+}++void+cpArrayDeleteObj(cpArray *arr, void *obj)+{+ for(int i=0; i<arr->num; i++){+ if(arr->arr[i] == obj){+ arr->num--;+ + arr->arr[i] = arr->arr[arr->num];+ arr->arr[arr->num] = NULL;+ + return;+ }+ }+}++void+cpArrayFreeEach(cpArray *arr, void (freeFunc)(void*))+{+ for(int i=0; i<arr->num; i++) freeFunc(arr->arr[i]);+}++cpBool+cpArrayContains(cpArray *arr, void *ptr)+{+ for(int i=0; i<arr->num; i++)+ if(arr->arr[i] == ptr) return cpTrue;+ + return cpFalse;+}
+ Chipmunk2D-7.0.2/src/cpBBTree.c view
@@ -0,0 +1,896 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "stdlib.h"+#include "stdio.h"++#include "chipmunk/chipmunk_private.h"++static inline cpSpatialIndexClass *Klass();++typedef struct Node Node;+typedef struct Pair Pair;++struct cpBBTree {+ cpSpatialIndex spatialIndex;+ cpBBTreeVelocityFunc velocityFunc;+ + cpHashSet *leaves;+ Node *root;+ + Node *pooledNodes;+ Pair *pooledPairs;+ cpArray *allocatedBuffers;+ + cpTimestamp stamp;+};++struct Node {+ void *obj;+ cpBB bb;+ Node *parent;+ + union {+ // Internal nodes+ struct { Node *a, *b; } children;+ + // Leaves+ struct {+ cpTimestamp stamp;+ Pair *pairs;+ } leaf;+ } node;+};++// Can't use anonymous unions and still get good x-compiler compatability+#define A node.children.a+#define B node.children.b+#define STAMP node.leaf.stamp+#define PAIRS node.leaf.pairs++typedef struct Thread {+ Pair *prev;+ Node *leaf;+ Pair *next;+} Thread;++struct Pair {+ Thread a, b;+ cpCollisionID id;+};++//MARK: Misc Functions++static inline cpBB+GetBB(cpBBTree *tree, void *obj)+{+ cpBB bb = tree->spatialIndex.bbfunc(obj);+ + cpBBTreeVelocityFunc velocityFunc = tree->velocityFunc;+ if(velocityFunc){+ cpFloat coef = 0.1f;+ cpFloat x = (bb.r - bb.l)*coef;+ cpFloat y = (bb.t - bb.b)*coef;+ + cpVect v = cpvmult(velocityFunc(obj), 0.1f);+ return cpBBNew(bb.l + cpfmin(-x, v.x), bb.b + cpfmin(-y, v.y), bb.r + cpfmax(x, v.x), bb.t + cpfmax(y, v.y));+ } else {+ return bb;+ }+}++static inline cpBBTree *+GetTree(cpSpatialIndex *index)+{+ return (index && index->klass == Klass() ? (cpBBTree *)index : NULL);+}++static inline Node *+GetRootIfTree(cpSpatialIndex *index){+ return (index && index->klass == Klass() ? ((cpBBTree *)index)->root : NULL);+}++static inline cpBBTree *+GetMasterTree(cpBBTree *tree)+{+ cpBBTree *dynamicTree = GetTree(tree->spatialIndex.dynamicIndex);+ return (dynamicTree ? dynamicTree : tree);+}++static inline void+IncrementStamp(cpBBTree *tree)+{+ cpBBTree *dynamicTree = GetTree(tree->spatialIndex.dynamicIndex);+ if(dynamicTree){+ dynamicTree->stamp++;+ } else {+ tree->stamp++;+ }+}++//MARK: Pair/Thread Functions++static void+PairRecycle(cpBBTree *tree, Pair *pair)+{+ // Share the pool of the master tree.+ // TODO: would be lovely to move the pairs stuff into an external data structure.+ tree = GetMasterTree(tree);+ + pair->a.next = tree->pooledPairs;+ tree->pooledPairs = pair;+}++static Pair *+PairFromPool(cpBBTree *tree)+{+ // Share the pool of the master tree.+ // TODO: would be lovely to move the pairs stuff into an external data structure.+ tree = GetMasterTree(tree);+ + Pair *pair = tree->pooledPairs;+ + if(pair){+ tree->pooledPairs = pair->a.next;+ return pair;+ } else {+ // Pool is exhausted, make more+ int count = CP_BUFFER_BYTES/sizeof(Pair);+ cpAssertHard(count, "Internal Error: Buffer size is too small.");+ + Pair *buffer = (Pair *)cpcalloc(1, CP_BUFFER_BYTES);+ cpArrayPush(tree->allocatedBuffers, buffer);+ + // push all but the first one, return the first instead+ for(int i=1; i<count; i++) PairRecycle(tree, buffer + i);+ return buffer;+ }+}++static inline void+ThreadUnlink(Thread thread)+{+ Pair *next = thread.next;+ Pair *prev = thread.prev;+ + if(next){+ if(next->a.leaf == thread.leaf) next->a.prev = prev; else next->b.prev = prev;+ }+ + if(prev){+ if(prev->a.leaf == thread.leaf) prev->a.next = next; else prev->b.next = next;+ } else {+ thread.leaf->PAIRS = next;+ }+}++static void+PairsClear(Node *leaf, cpBBTree *tree)+{+ Pair *pair = leaf->PAIRS;+ leaf->PAIRS = NULL;+ + while(pair){+ if(pair->a.leaf == leaf){+ Pair *next = pair->a.next;+ ThreadUnlink(pair->b);+ PairRecycle(tree, pair);+ pair = next;+ } else {+ Pair *next = pair->b.next;+ ThreadUnlink(pair->a);+ PairRecycle(tree, pair);+ pair = next;+ }+ }+}++static void+PairInsert(Node *a, Node *b, cpBBTree *tree)+{+ Pair *nextA = a->PAIRS, *nextB = b->PAIRS;+ Pair *pair = PairFromPool(tree);+ Pair temp = {{NULL, a, nextA},{NULL, b, nextB}, 0};+ + a->PAIRS = b->PAIRS = pair;+ *pair = temp;+ + if(nextA){+ if(nextA->a.leaf == a) nextA->a.prev = pair; else nextA->b.prev = pair;+ }+ + if(nextB){+ if(nextB->a.leaf == b) nextB->a.prev = pair; else nextB->b.prev = pair;+ }+}+++//MARK: Node Functions++static void+NodeRecycle(cpBBTree *tree, Node *node)+{+ node->parent = tree->pooledNodes;+ tree->pooledNodes = node;+}++static Node *+NodeFromPool(cpBBTree *tree)+{+ Node *node = tree->pooledNodes;+ + if(node){+ tree->pooledNodes = node->parent;+ return node;+ } else {+ // Pool is exhausted, make more+ int count = CP_BUFFER_BYTES/sizeof(Node);+ cpAssertHard(count, "Internal Error: Buffer size is too small.");+ + Node *buffer = (Node *)cpcalloc(1, CP_BUFFER_BYTES);+ cpArrayPush(tree->allocatedBuffers, buffer);+ + // push all but the first one, return the first instead+ for(int i=1; i<count; i++) NodeRecycle(tree, buffer + i);+ return buffer;+ }+}++static inline void+NodeSetA(Node *node, Node *value)+{+ node->A = value;+ value->parent = node;+}++static inline void+NodeSetB(Node *node, Node *value)+{+ node->B = value;+ value->parent = node;+}++static Node *+NodeNew(cpBBTree *tree, Node *a, Node *b)+{+ Node *node = NodeFromPool(tree);+ + node->obj = NULL;+ node->bb = cpBBMerge(a->bb, b->bb);+ node->parent = NULL;+ + NodeSetA(node, a);+ NodeSetB(node, b);+ + return node;+}++static inline cpBool+NodeIsLeaf(Node *node)+{+ return (node->obj != NULL);+}++static inline Node *+NodeOther(Node *node, Node *child)+{+ return (node->A == child ? node->B : node->A);+}++static inline void+NodeReplaceChild(Node *parent, Node *child, Node *value, cpBBTree *tree)+{+ cpAssertSoft(!NodeIsLeaf(parent), "Internal Error: Cannot replace child of a leaf.");+ cpAssertSoft(child == parent->A || child == parent->B, "Internal Error: Node is not a child of parent.");+ + if(parent->A == child){+ NodeRecycle(tree, parent->A);+ NodeSetA(parent, value);+ } else {+ NodeRecycle(tree, parent->B);+ NodeSetB(parent, value);+ }+ + for(Node *node=parent; node; node = node->parent){+ node->bb = cpBBMerge(node->A->bb, node->B->bb);+ }+}++//MARK: Subtree Functions++static inline cpFloat+cpBBProximity(cpBB a, cpBB b)+{+ return cpfabs(a.l + a.r - b.l - b.r) + cpfabs(a.b + a.t - b.b - b.t);+}++static Node *+SubtreeInsert(Node *subtree, Node *leaf, cpBBTree *tree)+{+ if(subtree == NULL){+ return leaf;+ } else if(NodeIsLeaf(subtree)){+ return NodeNew(tree, leaf, subtree);+ } else {+ cpFloat cost_a = cpBBArea(subtree->B->bb) + cpBBMergedArea(subtree->A->bb, leaf->bb);+ cpFloat cost_b = cpBBArea(subtree->A->bb) + cpBBMergedArea(subtree->B->bb, leaf->bb);+ + if(cost_a == cost_b){+ cost_a = cpBBProximity(subtree->A->bb, leaf->bb);+ cost_b = cpBBProximity(subtree->B->bb, leaf->bb);+ }+ + if(cost_b < cost_a){+ NodeSetB(subtree, SubtreeInsert(subtree->B, leaf, tree));+ } else {+ NodeSetA(subtree, SubtreeInsert(subtree->A, leaf, tree));+ }+ + subtree->bb = cpBBMerge(subtree->bb, leaf->bb);+ return subtree;+ }+}++static void+SubtreeQuery(Node *subtree, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data)+{+ if(cpBBIntersects(subtree->bb, bb)){+ if(NodeIsLeaf(subtree)){+ func(obj, subtree->obj, 0, data);+ } else {+ SubtreeQuery(subtree->A, obj, bb, func, data);+ SubtreeQuery(subtree->B, obj, bb, func, data);+ }+ }+}+++static cpFloat+SubtreeSegmentQuery(Node *subtree, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data)+{+ if(NodeIsLeaf(subtree)){+ return func(obj, subtree->obj, data);+ } else {+ cpFloat t_a = cpBBSegmentQuery(subtree->A->bb, a, b);+ cpFloat t_b = cpBBSegmentQuery(subtree->B->bb, a, b);+ + if(t_a < t_b){+ if(t_a < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->A, obj, a, b, t_exit, func, data));+ if(t_b < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->B, obj, a, b, t_exit, func, data));+ } else {+ if(t_b < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->B, obj, a, b, t_exit, func, data));+ if(t_a < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->A, obj, a, b, t_exit, func, data));+ }+ + return t_exit;+ }+}++static void+SubtreeRecycle(cpBBTree *tree, Node *node)+{+ if(!NodeIsLeaf(node)){+ SubtreeRecycle(tree, node->A);+ SubtreeRecycle(tree, node->B);+ NodeRecycle(tree, node);+ }+}++static inline Node *+SubtreeRemove(Node *subtree, Node *leaf, cpBBTree *tree)+{+ if(leaf == subtree){+ return NULL;+ } else {+ Node *parent = leaf->parent;+ if(parent == subtree){+ Node *other = NodeOther(subtree, leaf);+ other->parent = subtree->parent;+ NodeRecycle(tree, subtree);+ return other;+ } else {+ NodeReplaceChild(parent->parent, parent, NodeOther(parent, leaf), tree);+ return subtree;+ }+ }+}++//MARK: Marking Functions++typedef struct MarkContext {+ cpBBTree *tree;+ Node *staticRoot;+ cpSpatialIndexQueryFunc func;+ void *data;+} MarkContext;++static void+MarkLeafQuery(Node *subtree, Node *leaf, cpBool left, MarkContext *context)+{+ if(cpBBIntersects(leaf->bb, subtree->bb)){+ if(NodeIsLeaf(subtree)){+ if(left){+ PairInsert(leaf, subtree, context->tree);+ } else {+ if(subtree->STAMP < leaf->STAMP) PairInsert(subtree, leaf, context->tree);+ context->func(leaf->obj, subtree->obj, 0, context->data);+ }+ } else {+ MarkLeafQuery(subtree->A, leaf, left, context);+ MarkLeafQuery(subtree->B, leaf, left, context);+ }+ }+}++static void+MarkLeaf(Node *leaf, MarkContext *context)+{+ cpBBTree *tree = context->tree;+ if(leaf->STAMP == GetMasterTree(tree)->stamp){+ Node *staticRoot = context->staticRoot;+ if(staticRoot) MarkLeafQuery(staticRoot, leaf, cpFalse, context);+ + for(Node *node = leaf; node->parent; node = node->parent){+ if(node == node->parent->A){+ MarkLeafQuery(node->parent->B, leaf, cpTrue, context);+ } else {+ MarkLeafQuery(node->parent->A, leaf, cpFalse, context);+ }+ }+ } else {+ Pair *pair = leaf->PAIRS;+ while(pair){+ if(leaf == pair->b.leaf){+ pair->id = context->func(pair->a.leaf->obj, leaf->obj, pair->id, context->data);+ pair = pair->b.next;+ } else {+ pair = pair->a.next;+ }+ }+ }+}++static void+MarkSubtree(Node *subtree, MarkContext *context)+{+ if(NodeIsLeaf(subtree)){+ MarkLeaf(subtree, context);+ } else {+ MarkSubtree(subtree->A, context);+ MarkSubtree(subtree->B, context); // TODO: Force TCO here?+ }+}++//MARK: Leaf Functions++static Node *+LeafNew(cpBBTree *tree, void *obj, cpBB bb)+{+ Node *node = NodeFromPool(tree);+ node->obj = obj;+ node->bb = GetBB(tree, obj);+ + node->parent = NULL;+ node->STAMP = 0;+ node->PAIRS = NULL;+ + return node;+}++static cpBool+LeafUpdate(Node *leaf, cpBBTree *tree)+{+ Node *root = tree->root;+ cpBB bb = tree->spatialIndex.bbfunc(leaf->obj);+ + if(!cpBBContainsBB(leaf->bb, bb)){+ leaf->bb = GetBB(tree, leaf->obj);+ + root = SubtreeRemove(root, leaf, tree);+ tree->root = SubtreeInsert(root, leaf, tree);+ + PairsClear(leaf, tree);+ leaf->STAMP = GetMasterTree(tree)->stamp;+ + return cpTrue;+ } else {+ return cpFalse;+ }+}++static cpCollisionID VoidQueryFunc(void *obj1, void *obj2, cpCollisionID id, void *data){return id;}++static void+LeafAddPairs(Node *leaf, cpBBTree *tree)+{+ cpSpatialIndex *dynamicIndex = tree->spatialIndex.dynamicIndex;+ if(dynamicIndex){+ Node *dynamicRoot = GetRootIfTree(dynamicIndex);+ if(dynamicRoot){+ cpBBTree *dynamicTree = GetTree(dynamicIndex);+ MarkContext context = {dynamicTree, NULL, NULL, NULL};+ MarkLeafQuery(dynamicRoot, leaf, cpTrue, &context);+ }+ } else {+ Node *staticRoot = GetRootIfTree(tree->spatialIndex.staticIndex);+ MarkContext context = {tree, staticRoot, VoidQueryFunc, NULL};+ MarkLeaf(leaf, &context);+ }+}++//MARK: Memory Management Functions++cpBBTree *+cpBBTreeAlloc(void)+{+ return (cpBBTree *)cpcalloc(1, sizeof(cpBBTree));+}++static int+leafSetEql(void *obj, Node *node)+{+ return (obj == node->obj);+}++static void *+leafSetTrans(void *obj, cpBBTree *tree)+{+ return LeafNew(tree, obj, tree->spatialIndex.bbfunc(obj));+}++cpSpatialIndex *+cpBBTreeInit(cpBBTree *tree, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)+{+ cpSpatialIndexInit((cpSpatialIndex *)tree, Klass(), bbfunc, staticIndex);+ + tree->velocityFunc = NULL;+ + tree->leaves = cpHashSetNew(0, (cpHashSetEqlFunc)leafSetEql);+ tree->root = NULL;+ + tree->pooledNodes = NULL;+ tree->allocatedBuffers = cpArrayNew(0);+ + tree->stamp = 0;+ + return (cpSpatialIndex *)tree;+}++void+cpBBTreeSetVelocityFunc(cpSpatialIndex *index, cpBBTreeVelocityFunc func)+{+ if(index->klass != Klass()){+ cpAssertWarn(cpFalse, "Ignoring cpBBTreeSetVelocityFunc() call to non-tree spatial index.");+ return;+ }+ + ((cpBBTree *)index)->velocityFunc = func;+}++cpSpatialIndex *+cpBBTreeNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)+{+ return cpBBTreeInit(cpBBTreeAlloc(), bbfunc, staticIndex);+}++static void+cpBBTreeDestroy(cpBBTree *tree)+{+ cpHashSetFree(tree->leaves);+ + if(tree->allocatedBuffers) cpArrayFreeEach(tree->allocatedBuffers, cpfree);+ cpArrayFree(tree->allocatedBuffers);+}++//MARK: Insert/Remove++static void+cpBBTreeInsert(cpBBTree *tree, void *obj, cpHashValue hashid)+{+ Node *leaf = (Node *)cpHashSetInsert(tree->leaves, hashid, obj, (cpHashSetTransFunc)leafSetTrans, tree);+ + Node *root = tree->root;+ tree->root = SubtreeInsert(root, leaf, tree);+ + leaf->STAMP = GetMasterTree(tree)->stamp;+ LeafAddPairs(leaf, tree);+ IncrementStamp(tree);+}++static void+cpBBTreeRemove(cpBBTree *tree, void *obj, cpHashValue hashid)+{+ Node *leaf = (Node *)cpHashSetRemove(tree->leaves, hashid, obj);+ + tree->root = SubtreeRemove(tree->root, leaf, tree);+ PairsClear(leaf, tree);+ NodeRecycle(tree, leaf);+}++static cpBool+cpBBTreeContains(cpBBTree *tree, void *obj, cpHashValue hashid)+{+ return (cpHashSetFind(tree->leaves, hashid, obj) != NULL);+}++//MARK: Reindex++static void LeafUpdateWrap(Node *leaf, cpBBTree *tree) {LeafUpdate(leaf, tree);}++static void+cpBBTreeReindexQuery(cpBBTree *tree, cpSpatialIndexQueryFunc func, void *data)+{+ if(!tree->root) return;+ + // LeafUpdate() may modify tree->root. Don't cache it.+ cpHashSetEach(tree->leaves, (cpHashSetIteratorFunc)LeafUpdateWrap, tree);+ + cpSpatialIndex *staticIndex = tree->spatialIndex.staticIndex;+ Node *staticRoot = (staticIndex && staticIndex->klass == Klass() ? ((cpBBTree *)staticIndex)->root : NULL);+ + MarkContext context = {tree, staticRoot, func, data};+ MarkSubtree(tree->root, &context);+ if(staticIndex && !staticRoot) cpSpatialIndexCollideStatic((cpSpatialIndex *)tree, staticIndex, func, data);+ + IncrementStamp(tree);+}++static void+cpBBTreeReindex(cpBBTree *tree)+{+ cpBBTreeReindexQuery(tree, VoidQueryFunc, NULL);+}++static void+cpBBTreeReindexObject(cpBBTree *tree, void *obj, cpHashValue hashid)+{+ Node *leaf = (Node *)cpHashSetFind(tree->leaves, hashid, obj);+ if(leaf){+ if(LeafUpdate(leaf, tree)) LeafAddPairs(leaf, tree);+ IncrementStamp(tree);+ }+}++//MARK: Query++static void+cpBBTreeSegmentQuery(cpBBTree *tree, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data)+{+ Node *root = tree->root;+ if(root) SubtreeSegmentQuery(root, obj, a, b, t_exit, func, data);+}++static void+cpBBTreeQuery(cpBBTree *tree, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data)+{+ if(tree->root) SubtreeQuery(tree->root, obj, bb, func, data);+}++//MARK: Misc++static int+cpBBTreeCount(cpBBTree *tree)+{+ return cpHashSetCount(tree->leaves);+}++typedef struct eachContext {+ cpSpatialIndexIteratorFunc func;+ void *data;+} eachContext;++static void each_helper(Node *node, eachContext *context){context->func(node->obj, context->data);}++static void+cpBBTreeEach(cpBBTree *tree, cpSpatialIndexIteratorFunc func, void *data)+{+ eachContext context = {func, data};+ cpHashSetEach(tree->leaves, (cpHashSetIteratorFunc)each_helper, &context);+}++static cpSpatialIndexClass klass = {+ (cpSpatialIndexDestroyImpl)cpBBTreeDestroy,+ + (cpSpatialIndexCountImpl)cpBBTreeCount,+ (cpSpatialIndexEachImpl)cpBBTreeEach,+ + (cpSpatialIndexContainsImpl)cpBBTreeContains,+ (cpSpatialIndexInsertImpl)cpBBTreeInsert,+ (cpSpatialIndexRemoveImpl)cpBBTreeRemove,+ + (cpSpatialIndexReindexImpl)cpBBTreeReindex,+ (cpSpatialIndexReindexObjectImpl)cpBBTreeReindexObject,+ (cpSpatialIndexReindexQueryImpl)cpBBTreeReindexQuery,+ + (cpSpatialIndexQueryImpl)cpBBTreeQuery,+ (cpSpatialIndexSegmentQueryImpl)cpBBTreeSegmentQuery,+};++static inline cpSpatialIndexClass *Klass(){return &klass;}+++//MARK: Tree Optimization++static int+cpfcompare(const cpFloat *a, const cpFloat *b){+ return (*a < *b ? -1 : (*b < *a ? 1 : 0));+}++static void+fillNodeArray(Node *node, Node ***cursor){+ (**cursor) = node;+ (*cursor)++;+}++static Node *+partitionNodes(cpBBTree *tree, Node **nodes, int count)+{+ if(count == 1){+ return nodes[0];+ } else if(count == 2) {+ return NodeNew(tree, nodes[0], nodes[1]);+ }+ + // Find the AABB for these nodes+ cpBB bb = nodes[0]->bb;+ for(int i=1; i<count; i++) bb = cpBBMerge(bb, nodes[i]->bb);+ + // Split it on it's longest axis+ cpBool splitWidth = (bb.r - bb.l > bb.t - bb.b);+ + // Sort the bounds and use the median as the splitting point+ cpFloat *bounds = (cpFloat *)cpcalloc(count*2, sizeof(cpFloat));+ if(splitWidth){+ for(int i=0; i<count; i++){+ bounds[2*i + 0] = nodes[i]->bb.l;+ bounds[2*i + 1] = nodes[i]->bb.r;+ }+ } else {+ for(int i=0; i<count; i++){+ bounds[2*i + 0] = nodes[i]->bb.b;+ bounds[2*i + 1] = nodes[i]->bb.t;+ }+ }+ + qsort(bounds, count*2, sizeof(cpFloat), (int (*)(const void *, const void *))cpfcompare);+ cpFloat split = (bounds[count - 1] + bounds[count])*0.5f; // use the medain as the split+ cpfree(bounds);++ // Generate the child BBs+ cpBB a = bb, b = bb;+ if(splitWidth) a.r = b.l = split; else a.t = b.b = split;+ + // Partition the nodes+ int right = count;+ for(int left=0; left < right;){+ Node *node = nodes[left];+ if(cpBBMergedArea(node->bb, b) < cpBBMergedArea(node->bb, a)){+// if(cpBBProximity(node->bb, b) < cpBBProximity(node->bb, a)){+ right--;+ nodes[left] = nodes[right];+ nodes[right] = node;+ } else {+ left++;+ }+ }+ + if(right == count){+ Node *node = NULL;+ for(int i=0; i<count; i++) node = SubtreeInsert(node, nodes[i], tree);+ return node;+ }+ + // Recurse and build the node!+ return NodeNew(tree,+ partitionNodes(tree, nodes, right),+ partitionNodes(tree, nodes + right, count - right)+ );+}++//static void+//cpBBTreeOptimizeIncremental(cpBBTree *tree, int passes)+//{+// for(int i=0; i<passes; i++){+// Node *root = tree->root;+// Node *node = root;+// int bit = 0;+// unsigned int path = tree->opath;+// +// while(!NodeIsLeaf(node)){+// node = (path&(1<<bit) ? node->a : node->b);+// bit = (bit + 1)&(sizeof(unsigned int)*8 - 1);+// }+// +// root = subtreeRemove(root, node, tree);+// tree->root = subtreeInsert(root, node, tree);+// }+//}++void+cpBBTreeOptimize(cpSpatialIndex *index)+{+ if(index->klass != &klass){+ cpAssertWarn(cpFalse, "Ignoring cpBBTreeOptimize() call to non-tree spatial index.");+ return;+ }+ + cpBBTree *tree = (cpBBTree *)index;+ Node *root = tree->root;+ if(!root) return;+ + int count = cpBBTreeCount(tree);+ Node **nodes = (Node **)cpcalloc(count, sizeof(Node *));+ Node **cursor = nodes;+ + cpHashSetEach(tree->leaves, (cpHashSetIteratorFunc)fillNodeArray, &cursor);+ + SubtreeRecycle(tree, root);+ tree->root = partitionNodes(tree, nodes, count);+ cpfree(nodes);+}++//MARK: Debug Draw++//#define CP_BBTREE_DEBUG_DRAW+#ifdef CP_BBTREE_DEBUG_DRAW+#include "OpenGL/gl.h"+#include "OpenGL/glu.h"+#include <GLUT/glut.h>++static void+NodeRender(Node *node, int depth)+{+ if(!NodeIsLeaf(node) && depth <= 10){+ NodeRender(node->a, depth + 1);+ NodeRender(node->b, depth + 1);+ }+ + cpBB bb = node->bb;+ +// GLfloat v = depth/2.0f; +// glColor3f(1.0f - v, v, 0.0f);+ glLineWidth(cpfmax(5.0f - depth, 1.0f));+ glBegin(GL_LINES); {+ glVertex2f(bb.l, bb.b);+ glVertex2f(bb.l, bb.t);+ + glVertex2f(bb.l, bb.t);+ glVertex2f(bb.r, bb.t);+ + glVertex2f(bb.r, bb.t);+ glVertex2f(bb.r, bb.b);+ + glVertex2f(bb.r, bb.b);+ glVertex2f(bb.l, bb.b);+ }; glEnd();+}++void+cpBBTreeRenderDebug(cpSpatialIndex *index){+ if(index->klass != &klass){+ cpAssertWarn(cpFalse, "Ignoring cpBBTreeRenderDebug() call to non-tree spatial index.");+ return;+ }+ + cpBBTree *tree = (cpBBTree *)index;+ if(tree->root) NodeRender(tree->root, 0);+}+#endif
+ Chipmunk2D-7.0.2/src/cpBody.c view
@@ -0,0 +1,626 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include <float.h>+#include <stdarg.h>++#include "chipmunk/chipmunk_private.h"++cpBody*+cpBodyAlloc(void)+{+ return (cpBody *)cpcalloc(1, sizeof(cpBody));+}++cpBody *+cpBodyInit(cpBody *body, cpFloat mass, cpFloat moment)+{+ body->space = NULL;+ body->shapeList = NULL;+ body->arbiterList = NULL;+ body->constraintList = NULL;+ + body->velocity_func = cpBodyUpdateVelocity;+ body->position_func = cpBodyUpdatePosition;+ + body->sleeping.root = NULL;+ body->sleeping.next = NULL;+ body->sleeping.idleTime = 0.0f;+ + body->p = cpvzero;+ body->v = cpvzero;+ body->f = cpvzero;+ + body->w = 0.0f;+ body->t = 0.0f;+ + body->v_bias = cpvzero;+ body->w_bias = 0.0f;+ + body->userData = NULL;+ + // Setters must be called after full initialization so the sanity checks don't assert on garbage data.+ cpBodySetMass(body, mass);+ cpBodySetMoment(body, moment);+ cpBodySetAngle(body, 0.0f);+ + return body;+}++cpBody*+cpBodyNew(cpFloat mass, cpFloat moment)+{+ return cpBodyInit(cpBodyAlloc(), mass, moment);+}++cpBody*+cpBodyNewKinematic()+{+ cpBody *body = cpBodyNew(0.0f, 0.0f);+ cpBodySetType(body, CP_BODY_TYPE_KINEMATIC);+ + return body;+}++cpBody*+cpBodyNewStatic()+{+ cpBody *body = cpBodyNew(0.0f, 0.0f);+ cpBodySetType(body, CP_BODY_TYPE_STATIC);+ + return body;+}++void cpBodyDestroy(cpBody *body){}++void+cpBodyFree(cpBody *body)+{+ if(body){+ cpBodyDestroy(body);+ cpfree(body);+ }+}++#ifdef NDEBUG+ #define cpAssertSaneBody(body)+#else+ static void cpv_assert_nan(cpVect v, char *message){cpAssertHard(v.x == v.x && v.y == v.y, message);}+ static void cpv_assert_infinite(cpVect v, char *message){cpAssertHard(cpfabs(v.x) != INFINITY && cpfabs(v.y) != INFINITY, message);}+ static void cpv_assert_sane(cpVect v, char *message){cpv_assert_nan(v, message); cpv_assert_infinite(v, message);}+ + static void+ cpBodySanityCheck(const cpBody *body)+ {+ cpAssertHard(body->m == body->m && body->m_inv == body->m_inv, "Body's mass is NaN.");+ cpAssertHard(body->i == body->i && body->i_inv == body->i_inv, "Body's moment is NaN.");+ cpAssertHard(body->m >= 0.0f, "Body's mass is negative.");+ cpAssertHard(body->i >= 0.0f, "Body's moment is negative.");+ + cpv_assert_sane(body->p, "Body's position is invalid.");+ cpv_assert_sane(body->v, "Body's velocity is invalid.");+ cpv_assert_sane(body->f, "Body's force is invalid.");++ cpAssertHard(body->a == body->a && cpfabs(body->a) != INFINITY, "Body's angle is invalid.");+ cpAssertHard(body->w == body->w && cpfabs(body->w) != INFINITY, "Body's angular velocity is invalid.");+ cpAssertHard(body->t == body->t && cpfabs(body->t) != INFINITY, "Body's torque is invalid.");+ }+ + #define cpAssertSaneBody(body) cpBodySanityCheck(body)+#endif++cpBool+cpBodyIsSleeping(const cpBody *body)+{+ return (body->sleeping.root != ((cpBody*)0));+}++cpBodyType+cpBodyGetType(cpBody *body)+{+ if(body->sleeping.idleTime == INFINITY){+ return CP_BODY_TYPE_STATIC;+ } else if(body->m == INFINITY){+ return CP_BODY_TYPE_KINEMATIC;+ } else {+ return CP_BODY_TYPE_DYNAMIC;+ }+}++void+cpBodySetType(cpBody *body, cpBodyType type)+{+ cpBodyType oldType = cpBodyGetType(body);+ if(oldType == type) return;+ + // Static bodies have their idle timers set to infinity.+ // Non-static bodies should have their idle timer reset.+ body->sleeping.idleTime = (type == CP_BODY_TYPE_STATIC ? INFINITY : 0.0f);+ + if(type == CP_BODY_TYPE_DYNAMIC){+ body->m = body->i = 0.0f;+ body->m_inv = body->i_inv = INFINITY;+ + cpBodyAccumulateMassFromShapes(body);+ } else {+ body->m = body->i = INFINITY;+ body->m_inv = body->i_inv = 0.0f;+ + body->v = cpvzero;+ body->w = 0.0f;+ }+ + // If the body is added to a space already, we'll need to update some space data structures.+ cpSpace *space = cpBodyGetSpace(body);+ if(space != NULL){+ cpAssertSpaceUnlocked(space);+ + if(oldType == CP_BODY_TYPE_STATIC){+ // TODO This is probably not necessary+// cpBodyActivateStatic(body, NULL);+ } else {+ cpBodyActivate(body);+ }+ + // Move the bodies to the correct array.+ cpArray *fromArray = cpSpaceArrayForBodyType(space, oldType);+ cpArray *toArray = cpSpaceArrayForBodyType(space, type);+ if(fromArray != toArray){+ cpArrayDeleteObj(fromArray, body);+ cpArrayPush(toArray, body);+ }+ + // Move the body's shapes to the correct spatial index.+ cpSpatialIndex *fromIndex = (oldType == CP_BODY_TYPE_STATIC ? space->staticShapes : space->dynamicShapes);+ cpSpatialIndex *toIndex = (type == CP_BODY_TYPE_STATIC ? space->staticShapes : space->dynamicShapes);+ if(fromIndex != toIndex){+ CP_BODY_FOREACH_SHAPE(body, shape){+ cpSpatialIndexRemove(fromIndex, shape, shape->hashid);+ cpSpatialIndexInsert(toIndex, shape, shape->hashid);+ }+ }+ }+}++++// Should *only* be called when shapes with mass info are modified, added or removed.+void+cpBodyAccumulateMassFromShapes(cpBody *body)+{+ if(body == NULL || cpBodyGetType(body) != CP_BODY_TYPE_DYNAMIC) return;+ + // Reset the body's mass data.+ body->m = body->i = 0.0f;+ body->cog = cpvzero;+ + // Cache the position to realign it at the end.+ cpVect pos = cpBodyGetPosition(body);+ + // Accumulate mass from shapes.+ CP_BODY_FOREACH_SHAPE(body, shape){+ struct cpShapeMassInfo *info = &shape->massInfo;+ cpFloat m = info->m;+ + if(m > 0.0f){+ cpFloat msum = body->m + m;+ + body->i += m*info->i + cpvdistsq(body->cog, info->cog)*(m*body->m)/msum;+ body->cog = cpvlerp(body->cog, info->cog, m/msum);+ body->m = msum;+ }+ }+ + // Recalculate the inverses.+ body->m_inv = 1.0f/body->m;+ body->i_inv = 1.0f/body->i;+ + // Realign the body since the CoG has probably moved.+ cpBodySetPosition(body, pos);+ cpAssertSaneBody(body);+}++cpSpace *+cpBodyGetSpace(const cpBody *body)+{+ return body->space;+}++cpFloat+cpBodyGetMass(const cpBody *body)+{+ return body->m;+}++void+cpBodySetMass(cpBody *body, cpFloat mass)+{+ cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "You cannot set the mass of kinematic or static bodies.");+ cpAssertHard(0.0f <= mass && mass < INFINITY, "Mass must be positive and finite.");+ + cpBodyActivate(body);+ body->m = mass;+ body->m_inv = 1.0f/mass;+ cpAssertSaneBody(body);+}++cpFloat+cpBodyGetMoment(const cpBody *body)+{+ return body->i;+}++void+cpBodySetMoment(cpBody *body, cpFloat moment)+{+ cpAssertHard(moment >= 0.0f, "Moment of Inertia must be positive.");+ + cpBodyActivate(body);+ body->i = moment;+ body->i_inv = 1.0f/moment;+ cpAssertSaneBody(body);+}++cpVect+cpBodyGetRotation(const cpBody *body)+{+ return cpv(body->transform.a, body->transform.b);+}++void+cpBodyAddShape(cpBody *body, cpShape *shape)+{+ cpShape *next = body->shapeList;+ if(next) next->prev = shape;+ + shape->next = next;+ body->shapeList = shape;+ + if(shape->massInfo.m > 0.0f){+ cpBodyAccumulateMassFromShapes(body);+ }+}++void+cpBodyRemoveShape(cpBody *body, cpShape *shape)+{+ cpShape *prev = shape->prev;+ cpShape *next = shape->next;+ + if(prev){+ prev->next = next;+ } else {+ body->shapeList = next;+ }+ + if(next){+ next->prev = prev;+ }+ + shape->prev = NULL;+ shape->next = NULL;+ + if(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC && shape->massInfo.m > 0.0f){+ cpBodyAccumulateMassFromShapes(body);+ }+}++static cpConstraint *+filterConstraints(cpConstraint *node, cpBody *body, cpConstraint *filter)+{+ if(node == filter){+ return cpConstraintNext(node, body);+ } else if(node->a == body){+ node->next_a = filterConstraints(node->next_a, body, filter);+ } else {+ node->next_b = filterConstraints(node->next_b, body, filter);+ }+ + return node;+}++void+cpBodyRemoveConstraint(cpBody *body, cpConstraint *constraint)+{+ body->constraintList = filterConstraints(body->constraintList, body, constraint);+}++// 'p' is the position of the CoG+static void+SetTransform(cpBody *body, cpVect p, cpFloat a)+{+ cpVect rot = cpvforangle(a);+ cpVect c = body->cog;+ + body->transform = cpTransformNewTranspose(+ rot.x, -rot.y, p.x - (c.x*rot.x - c.y*rot.y),+ rot.y, rot.x, p.y - (c.x*rot.y + c.y*rot.x)+ );+}++static inline cpFloat+SetAngle(cpBody *body, cpFloat a)+{+ body->a = a;+ cpAssertSaneBody(body);+ + return a;+}++cpVect+cpBodyGetPosition(const cpBody *body)+{+ return cpTransformPoint(body->transform, cpvzero);+}++void+cpBodySetPosition(cpBody *body, cpVect position)+{+ cpBodyActivate(body);+ cpVect p = body->p = cpvadd(cpTransformVect(body->transform, body->cog), position);+ cpAssertSaneBody(body);+ + SetTransform(body, p, body->a);+}++cpVect+cpBodyGetCenterOfGravity(const cpBody *body)+{+ return body->cog;+}++void+cpBodySetCenterOfGravity(cpBody *body, cpVect cog)+{+ cpBodyActivate(body);+ body->cog = cog;+ cpAssertSaneBody(body);+}++cpVect+cpBodyGetVelocity(const cpBody *body)+{+ return body->v;+}++void+cpBodySetVelocity(cpBody *body, cpVect velocity)+{+ cpBodyActivate(body);+ body->v = velocity;+ cpAssertSaneBody(body);+}++cpVect+cpBodyGetForce(const cpBody *body)+{+ return body->f;+}++void+cpBodySetForce(cpBody *body, cpVect force)+{+ cpBodyActivate(body);+ body->f = force;+ cpAssertSaneBody(body);+}++cpFloat+cpBodyGetAngle(const cpBody *body)+{+ return body->a;+}++void+cpBodySetAngle(cpBody *body, cpFloat angle)+{+ cpBodyActivate(body);+ SetAngle(body, angle);+ + SetTransform(body, body->p, angle);+}++cpFloat+cpBodyGetAngularVelocity(const cpBody *body)+{+ return body->w;+}++void+cpBodySetAngularVelocity(cpBody *body, cpFloat angularVelocity)+{+ cpBodyActivate(body);+ body->w = angularVelocity;+ cpAssertSaneBody(body);+}++cpFloat+cpBodyGetTorque(const cpBody *body)+{+ return body->t;+}++void+cpBodySetTorque(cpBody *body, cpFloat torque)+{+ cpBodyActivate(body);+ body->t = torque;+ cpAssertSaneBody(body);+}++cpDataPointer+cpBodyGetUserData(const cpBody *body)+{+ return body->userData;+}++void+cpBodySetUserData(cpBody *body, cpDataPointer userData)+{+ body->userData = userData;+}++void+cpBodySetVelocityUpdateFunc(cpBody *body, cpBodyVelocityFunc velocityFunc)+{+ body->velocity_func = velocityFunc;+}++void+cpBodySetPositionUpdateFunc(cpBody *body, cpBodyPositionFunc positionFunc)+{+ body->position_func = positionFunc;+}++void+cpBodyUpdateVelocity(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt)+{+ // Skip kinematic bodies.+ if(cpBodyGetType(body) == CP_BODY_TYPE_KINEMATIC) return;+ + cpAssertSoft(body->m > 0.0f && body->i > 0.0f, "Body's mass and moment must be positive to simulate. (Mass: %f Moment: %f)", body->m, body->i);+ + body->v = cpvadd(cpvmult(body->v, damping), cpvmult(cpvadd(gravity, cpvmult(body->f, body->m_inv)), dt));+ body->w = body->w*damping + body->t*body->i_inv*dt;+ + // Reset forces.+ body->f = cpvzero;+ body->t = 0.0f;+ + cpAssertSaneBody(body);+}++void+cpBodyUpdatePosition(cpBody *body, cpFloat dt)+{+ cpVect p = body->p = cpvadd(body->p, cpvmult(cpvadd(body->v, body->v_bias), dt));+ cpFloat a = SetAngle(body, body->a + (body->w + body->w_bias)*dt);+ SetTransform(body, p, a);+ + body->v_bias = cpvzero;+ body->w_bias = 0.0f;+ + cpAssertSaneBody(body);+}++cpVect+cpBodyLocalToWorld(const cpBody *body, const cpVect point)+{+ return cpTransformPoint(body->transform, point);+}++cpVect+cpBodyWorldToLocal(const cpBody *body, const cpVect point)+{+ return cpTransformPoint(cpTransformRigidInverse(body->transform), point);+}++void+cpBodyApplyForceAtWorldPoint(cpBody *body, cpVect force, cpVect point)+{+ cpBodyActivate(body);+ body->f = cpvadd(body->f, force);+ + cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog));+ body->t += cpvcross(r, force);+}++void+cpBodyApplyForceAtLocalPoint(cpBody *body, cpVect force, cpVect point)+{+ cpBodyApplyForceAtWorldPoint(body, cpTransformVect(body->transform, force), cpTransformPoint(body->transform, point));+}++void+cpBodyApplyImpulseAtWorldPoint(cpBody *body, cpVect impulse, cpVect point)+{+ cpBodyActivate(body);+ + cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog));+ apply_impulse(body, impulse, r);+}++void+cpBodyApplyImpulseAtLocalPoint(cpBody *body, cpVect impulse, cpVect point)+{+ cpBodyApplyImpulseAtWorldPoint(body, cpTransformVect(body->transform, impulse), cpTransformPoint(body->transform, point));+}++cpVect+cpBodyGetVelocityAtLocalPoint(const cpBody *body, cpVect point)+{+ cpVect r = cpTransformVect(body->transform, cpvsub(point, body->cog));+ return cpvadd(body->v, cpvmult(cpvperp(r), body->w));+}++cpVect+cpBodyGetVelocityAtWorldPoint(const cpBody *body, cpVect point)+{+ cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog));+ return cpvadd(body->v, cpvmult(cpvperp(r), body->w));+}++cpFloat+cpBodyKineticEnergy(const cpBody *body)+{+ // Need to do some fudging to avoid NaNs+ cpFloat vsq = cpvdot(body->v, body->v);+ cpFloat wsq = body->w*body->w;+ return (vsq ? vsq*body->m : 0.0f) + (wsq ? wsq*body->i : 0.0f);+}++void+cpBodyEachShape(cpBody *body, cpBodyShapeIteratorFunc func, void *data)+{+ cpShape *shape = body->shapeList;+ while(shape){+ cpShape *next = shape->next;+ func(body, shape, data);+ shape = next;+ }+}++void+cpBodyEachConstraint(cpBody *body, cpBodyConstraintIteratorFunc func, void *data)+{+ cpConstraint *constraint = body->constraintList;+ while(constraint){+ cpConstraint *next = cpConstraintNext(constraint, body);+ func(body, constraint, data);+ constraint = next;+ }+}++void+cpBodyEachArbiter(cpBody *body, cpBodyArbiterIteratorFunc func, void *data)+{+ cpArbiter *arb = body->arbiterList;+ while(arb){+ cpArbiter *next = cpArbiterNext(arb, body);+ + cpBool swapped = arb->swapped; {+ arb->swapped = (body == arb->body_b);+ func(body, arb, data);+ } arb->swapped = swapped;+ + arb = next;+ }+}
+ Chipmunk2D-7.0.2/src/cpCollision.c view
@@ -0,0 +1,726 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include <stdio.h>+#include <string.h>++#include "chipmunk/chipmunk_private.h"+#include "chipmunk/cpRobust.h"++#if DEBUG && 0+#include "ChipmunkDemo.h"+#define DRAW_ALL 0+#define DRAW_GJK (0 || DRAW_ALL)+#define DRAW_EPA (0 || DRAW_ALL)+#define DRAW_CLOSEST (0 || DRAW_ALL)+#define DRAW_CLIP (0 || DRAW_ALL)++#define PRINT_LOG 0+#endif++#define MAX_GJK_ITERATIONS 30+#define MAX_EPA_ITERATIONS 30+#define WARN_GJK_ITERATIONS 20+#define WARN_EPA_ITERATIONS 20++static inline void+cpCollisionInfoPushContact(struct cpCollisionInfo *info, cpVect p1, cpVect p2, cpHashValue hash)+{+ cpAssertSoft(info->count <= CP_MAX_CONTACTS_PER_ARBITER, "Internal error: Tried to push too many contacts.");+ + struct cpContact *con = &info->arr[info->count];+ con->r1 = p1;+ con->r2 = p2;+ con->hash = hash;+ + info->count++;+}++//MARK: Support Points and Edges:++// Support points are the maximal points on a shape's perimeter along a certain axis.+// The GJK and EPA algorithms use support points to iteratively sample the surface of the two shapes' minkowski difference.++static inline int+PolySupportPointIndex(const int count, const struct cpSplittingPlane *planes, const cpVect n)+{+ cpFloat max = -INFINITY;+ int index = 0;+ + for(int i=0; i<count; i++){+ cpVect v = planes[i].v0;+ cpFloat d = cpvdot(v, n);+ if(d > max){+ max = d;+ index = i;+ }+ }+ + return index;+}++struct SupportPoint {+ cpVect p;+ // Save an index of the point so it can be cheaply looked up as a starting point for the next frame.+ cpCollisionID index;+};++static inline struct SupportPoint+SupportPointNew(cpVect p, cpCollisionID index)+{+ struct SupportPoint point = {p, index};+ return point;+}++typedef struct SupportPoint (*SupportPointFunc)(const cpShape *shape, const cpVect n);++static inline struct SupportPoint+CircleSupportPoint(const cpCircleShape *circle, const cpVect n)+{+ return SupportPointNew(circle->tc, 0);+}++static inline struct SupportPoint+SegmentSupportPoint(const cpSegmentShape *seg, const cpVect n)+{+ if(cpvdot(seg->ta, n) > cpvdot(seg->tb, n)){+ return SupportPointNew(seg->ta, 0);+ } else {+ return SupportPointNew(seg->tb, 1);+ }+}++static inline struct SupportPoint+PolySupportPoint(const cpPolyShape *poly, const cpVect n)+{+ const struct cpSplittingPlane *planes = poly->planes;+ int i = PolySupportPointIndex(poly->count, planes, n);+ return SupportPointNew(planes[i].v0, i);+}++// A point on the surface of two shape's minkowski difference.+struct MinkowskiPoint {+ // Cache the two original support points.+ cpVect a, b;+ // b - a+ cpVect ab;+ // Concatenate the two support point indexes.+ cpCollisionID id;+};++static inline struct MinkowskiPoint+MinkowskiPointNew(const struct SupportPoint a, const struct SupportPoint b)+{+ struct MinkowskiPoint point = {a.p, b.p, cpvsub(b.p, a.p), (a.index & 0xFF)<<8 | (b.index & 0xFF)};+ return point;+}++struct SupportContext {+ const cpShape *shape1, *shape2;+ SupportPointFunc func1, func2;+};++// Calculate the maximal point on the minkowski difference of two shapes along a particular axis.+static inline struct MinkowskiPoint+Support(const struct SupportContext *ctx, const cpVect n)+{+ struct SupportPoint a = ctx->func1(ctx->shape1, cpvneg(n));+ struct SupportPoint b = ctx->func2(ctx->shape2, n);+ return MinkowskiPointNew(a, b);+}++struct EdgePoint {+ cpVect p;+ // Keep a hash value for Chipmunk's collision hashing mechanism.+ cpHashValue hash;+};++// Support edges are the edges of a polygon or segment shape that are in contact.+struct Edge {+ struct EdgePoint a, b;+ cpFloat r;+ cpVect n;+};++static struct Edge+SupportEdgeForPoly(const cpPolyShape *poly, const cpVect n)+{+ int count = poly->count;+ int i1 = PolySupportPointIndex(poly->count, poly->planes, n);+ + // TODO: get rid of mod eventually, very expensive on ARM+ int i0 = (i1 - 1 + count)%count;+ int i2 = (i1 + 1)%count;+ + const struct cpSplittingPlane *planes = poly->planes;+ cpHashValue hashid = poly->shape.hashid;+ if(cpvdot(n, planes[i1].n) > cpvdot(n, planes[i2].n)){+ struct Edge edge = {{planes[i0].v0, CP_HASH_PAIR(hashid, i0)}, {planes[i1].v0, CP_HASH_PAIR(hashid, i1)}, poly->r, planes[i1].n};+ return edge;+ } else {+ struct Edge edge = {{planes[i1].v0, CP_HASH_PAIR(hashid, i1)}, {planes[i2].v0, CP_HASH_PAIR(hashid, i2)}, poly->r, planes[i2].n};+ return edge;+ }+}++static struct Edge+SupportEdgeForSegment(const cpSegmentShape *seg, const cpVect n)+{+ cpHashValue hashid = seg->shape.hashid;+ if(cpvdot(seg->tn, n) > 0.0){+ struct Edge edge = {{seg->ta, CP_HASH_PAIR(hashid, 0)}, {seg->tb, CP_HASH_PAIR(hashid, 1)}, seg->r, seg->tn};+ return edge;+ } else {+ struct Edge edge = {{seg->tb, CP_HASH_PAIR(hashid, 1)}, {seg->ta, CP_HASH_PAIR(hashid, 0)}, seg->r, cpvneg(seg->tn)};+ return edge;+ }+}++// Find the closest p(t) to (0, 0) where p(t) = a*(1-t)/2 + b*(1+t)/2+// The range for t is [-1, 1] to avoid floating point issues if the parameters are swapped.+static inline cpFloat+ClosestT(const cpVect a, const cpVect b)+{+ cpVect delta = cpvsub(b, a);+ return -cpfclamp(cpvdot(delta, cpvadd(a, b))/cpvlengthsq(delta), -1.0f, 1.0f);+}++// Basically the same as cpvlerp(), except t = [-1, 1]+static inline cpVect+LerpT(const cpVect a, const cpVect b, const cpFloat t)+{+ cpFloat ht = 0.5f*t;+ return cpvadd(cpvmult(a, 0.5f - ht), cpvmult(b, 0.5f + ht));+}++// Closest points on the surface of two shapes.+struct ClosestPoints {+ // Surface points in absolute coordinates.+ cpVect a, b;+ // Minimum separating axis of the two shapes.+ cpVect n;+ // Signed distance between the points.+ cpFloat d;+ // Concatenation of the id's of the minkoski points.+ cpCollisionID id;+};++// Calculate the closest points on two shapes given the closest edge on their minkowski difference to (0, 0)+static inline struct ClosestPoints+ClosestPointsNew(const struct MinkowskiPoint v0, const struct MinkowskiPoint v1)+{+ // Find the closest p(t) on the minkowski difference to (0, 0)+ cpFloat t = ClosestT(v0.ab, v1.ab);+ cpVect p = LerpT(v0.ab, v1.ab, t);+ + // Interpolate the original support points using the same 't' value as above.+ // This gives you the closest surface points in absolute coordinates. NEAT!+ cpVect pa = LerpT(v0.a, v1.a, t);+ cpVect pb = LerpT(v0.b, v1.b, t);+ cpCollisionID id = (v0.id & 0xFFFF)<<16 | (v1.id & 0xFFFF);+ + // First try calculating the MSA from the minkowski difference edge.+ // This gives us a nice, accurate MSA when the surfaces are close together.+ cpVect delta = cpvsub(v1.ab, v0.ab);+ cpVect n = cpvnormalize(cpvrperp(delta));+ cpFloat d = cpvdot(n, p);+ + if(d <= 0.0f || (-1.0f < t && t < 1.0f)){+ // If the shapes are overlapping, or we have a regular vertex/edge collision, we are done.+ struct ClosestPoints points = {pa, pb, n, d, id};+ return points;+ } else {+ // Vertex/vertex collisions need special treatment since the MSA won't be shared with an axis of the minkowski difference.+ cpFloat d2 = cpvlength(p);+ cpVect n2 = cpvmult(p, 1.0f/(d2 + CPFLOAT_MIN));+ + struct ClosestPoints points = {pa, pb, n2, d2, id};+ return points;+ }+}++//MARK: EPA Functions++static inline cpFloat+ClosestDist(const cpVect v0,const cpVect v1)+{+ return cpvlengthsq(LerpT(v0, v1, ClosestT(v0, v1)));+}++// Recursive implementation of the EPA loop.+// Each recursion adds a point to the convex hull until it's known that we have the closest point on the surface.+static struct ClosestPoints+EPARecurse(const struct SupportContext *ctx, const int count, const struct MinkowskiPoint *hull, const int iteration)+{+ int mini = 0;+ cpFloat minDist = INFINITY;+ + // TODO: precalculate this when building the hull and save a step.+ // Find the closest segment hull[i] and hull[i + 1] to (0, 0)+ for(int j=0, i=count-1; j<count; i=j, j++){+ cpFloat d = ClosestDist(hull[i].ab, hull[j].ab);+ if(d < minDist){+ minDist = d;+ mini = i;+ }+ }+ + struct MinkowskiPoint v0 = hull[mini];+ struct MinkowskiPoint v1 = hull[(mini + 1)%count];+ cpAssertSoft(!cpveql(v0.ab, v1.ab), "Internal Error: EPA vertexes are the same (%d and %d)", mini, (mini + 1)%count);+ + // Check if there is a point on the minkowski difference beyond this edge.+ struct MinkowskiPoint p = Support(ctx, cpvperp(cpvsub(v1.ab, v0.ab)));+ +#if DRAW_EPA+ cpVect verts[count];+ for(int i=0; i<count; i++) verts[i] = hull[i].ab;+ + ChipmunkDebugDrawPolygon(count, verts, 0.0, RGBAColor(1, 1, 0, 1), RGBAColor(1, 1, 0, 0.25));+ ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 0, 0, 1));+ + ChipmunkDebugDrawDot(5, p.ab, LAColor(1, 1));+#endif+ + // The usual exit condition is a duplicated vertex.+ // Much faster to check the ids than to check the signed area.+ cpBool duplicate = (p.id == v0.id || p.id == v1.id);+ + if(!duplicate && cpCheckPointGreater(v0.ab, v1.ab, p.ab) && iteration < MAX_EPA_ITERATIONS){+ // Rebuild the convex hull by inserting p.+ struct MinkowskiPoint *hull2 = (struct MinkowskiPoint *)alloca((count + 1)*sizeof(struct MinkowskiPoint));+ int count2 = 1;+ hull2[0] = p;+ + for(int i=0; i<count; i++){+ int index = (mini + 1 + i)%count;+ + cpVect h0 = hull2[count2 - 1].ab;+ cpVect h1 = hull[index].ab;+ cpVect h2 = (i + 1 < count ? hull[(index + 1)%count] : p).ab;+ + if(cpCheckPointGreater(h0, h2, h1)){+ hull2[count2] = hull[index];+ count2++;+ }+ }+ + return EPARecurse(ctx, count2, hull2, iteration + 1);+ } else {+ // Could not find a new point to insert, so we have found the closest edge of the minkowski difference.+ cpAssertWarn(iteration < WARN_EPA_ITERATIONS, "High EPA iterations: %d", iteration);+ return ClosestPointsNew(v0, v1);+ }+}++// Find the closest points on the surface of two overlapping shapes using the EPA algorithm.+// EPA is called from GJK when two shapes overlap.+// This is a moderately expensive step! Avoid it by adding radii to your shapes so their inner polygons won't overlap.+static struct ClosestPoints+EPA(const struct SupportContext *ctx, const struct MinkowskiPoint v0, const struct MinkowskiPoint v1, const struct MinkowskiPoint v2)+{+ // TODO: allocate a NxM array here and do an in place convex hull reduction in EPARecurse?+ struct MinkowskiPoint hull[3] = {v0, v1, v2};+ return EPARecurse(ctx, 3, hull, 1);+}++//MARK: GJK Functions.++// Recursive implementation of the GJK loop.+static inline struct ClosestPoints+GJKRecurse(const struct SupportContext *ctx, const struct MinkowskiPoint v0, const struct MinkowskiPoint v1, const int iteration)+{+ if(iteration > MAX_GJK_ITERATIONS){+ cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration);+ return ClosestPointsNew(v0, v1);+ }+ + if(cpCheckPointGreater(v1.ab, v0.ab, cpvzero)){+ // Origin is behind axis. Flip and try again.+ return GJKRecurse(ctx, v1, v0, iteration);+ } else {+ cpFloat t = ClosestT(v0.ab, v1.ab);+ cpVect n = (-1.0f < t && t < 1.0f ? cpvperp(cpvsub(v1.ab, v0.ab)) : cpvneg(LerpT(v0.ab, v1.ab, t)));+ struct MinkowskiPoint p = Support(ctx, n);+ +#if DRAW_GJK+ ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 1, 1, 1));+ cpVect c = cpvlerp(v0.ab, v1.ab, 0.5);+ ChipmunkDebugDrawSegment(c, cpvadd(c, cpvmult(cpvnormalize(n), 5.0)), RGBAColor(1, 0, 0, 1));+ + ChipmunkDebugDrawDot(5.0, p.ab, LAColor(1, 1));+#endif+ + if(cpCheckPointGreater(p.ab, v0.ab, cpvzero) && cpCheckPointGreater(v1.ab, p.ab, cpvzero)){+ // The triangle v0, p, v1 contains the origin. Use EPA to find the MSA.+ cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK->EPA iterations: %d", iteration);+ return EPA(ctx, v0, p, v1);+ } else {+ if(cpCheckAxis(v0.ab, v1.ab, p.ab, n)){+ // The edge v0, v1 that we already have is the closest to (0, 0) since p was not closer.+ cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration);+ return ClosestPointsNew(v0, v1);+ } else {+ // p was closer to the origin than our existing edge.+ // Need to figure out which existing point to drop.+ if(ClosestDist(v0.ab, p.ab) < ClosestDist(p.ab, v1.ab)){+ return GJKRecurse(ctx, v0, p, iteration + 1);+ } else {+ return GJKRecurse(ctx, p, v1, iteration + 1);+ }+ }+ }+ }+}++// Get a SupportPoint from a cached shape and index.+static struct SupportPoint+ShapePoint(const cpShape *shape, const int i)+{+ switch(shape->klass->type){+ case CP_CIRCLE_SHAPE: {+ return SupportPointNew(((cpCircleShape *)shape)->tc, 0);+ } case CP_SEGMENT_SHAPE: {+ cpSegmentShape *seg = (cpSegmentShape *)shape;+ return SupportPointNew(i == 0 ? seg->ta : seg->tb, i);+ } case CP_POLY_SHAPE: {+ cpPolyShape *poly = (cpPolyShape *)shape;+ // Poly shapes may change vertex count.+ int index = (i < poly->count ? i : 0);+ return SupportPointNew(poly->planes[index].v0, index);+ } default: {+ return SupportPointNew(cpvzero, 0);+ }+ }+}++// Find the closest points between two shapes using the GJK algorithm.+static struct ClosestPoints+GJK(const struct SupportContext *ctx, cpCollisionID *id)+{+#if DRAW_GJK || DRAW_EPA+ int count1 = 1;+ int count2 = 1;+ + switch(ctx->shape1->klass->type){+ case CP_SEGMENT_SHAPE: count1 = 2; break;+ case CP_POLY_SHAPE: count1 = ((cpPolyShape *)ctx->shape1)->count; break;+ default: break;+ }+ + switch(ctx->shape2->klass->type){+ case CP_SEGMENT_SHAPE: count1 = 2; break;+ case CP_POLY_SHAPE: count2 = ((cpPolyShape *)ctx->shape2)->count; break;+ default: break;+ }+ + + // draw the minkowski difference origin+ cpVect origin = cpvzero;+ ChipmunkDebugDrawDot(5.0, origin, RGBAColor(1,0,0,1));+ + int mdiffCount = count1*count2;+ cpVect *mdiffVerts = alloca(mdiffCount*sizeof(cpVect));+ + for(int i=0; i<count1; i++){+ for(int j=0; j<count2; j++){+ cpVect v = cpvsub(ShapePoint(ctx->shape2, j).p, ShapePoint(ctx->shape1, i).p);+ mdiffVerts[i*count2 + j] = v;+ ChipmunkDebugDrawDot(2.0, v, RGBAColor(1, 0, 0, 1));+ }+ }+ + cpVect *hullVerts = alloca(mdiffCount*sizeof(cpVect));+ int hullCount = cpConvexHull(mdiffCount, mdiffVerts, hullVerts, NULL, 0.0);+ + ChipmunkDebugDrawPolygon(hullCount, hullVerts, 0.0, RGBAColor(1, 0, 0, 1), RGBAColor(1, 0, 0, 0.25));+#endif+ + struct MinkowskiPoint v0, v1;+ if(*id){+ // Use the minkowski points from the last frame as a starting point using the cached indexes.+ v0 = MinkowskiPointNew(ShapePoint(ctx->shape1, (*id>>24)&0xFF), ShapePoint(ctx->shape2, (*id>>16)&0xFF));+ v1 = MinkowskiPointNew(ShapePoint(ctx->shape1, (*id>> 8)&0xFF), ShapePoint(ctx->shape2, (*id )&0xFF));+ } else {+ // No cached indexes, use the shapes' bounding box centers as a guess for a starting axis.+ cpVect axis = cpvperp(cpvsub(cpBBCenter(ctx->shape1->bb), cpBBCenter(ctx->shape2->bb)));+ v0 = Support(ctx, axis);+ v1 = Support(ctx, cpvneg(axis));+ }+ + struct ClosestPoints points = GJKRecurse(ctx, v0, v1, 1);+ *id = points.id;+ return points;+}++//MARK: Contact Clipping++// Given two support edges, find contact point pairs on their surfaces.+static inline void+ContactPoints(const struct Edge e1, const struct Edge e2, const struct ClosestPoints points, struct cpCollisionInfo *info)+{+ cpFloat mindist = e1.r + e2.r;+ if(points.d <= mindist){+#ifdef DRAW_CLIP+ ChipmunkDebugDrawFatSegment(e1.a.p, e1.b.p, e1.r, RGBAColor(0, 1, 0, 1), LAColor(0, 0));+ ChipmunkDebugDrawFatSegment(e2.a.p, e2.b.p, e2.r, RGBAColor(1, 0, 0, 1), LAColor(0, 0));+#endif+ cpVect n = info->n = points.n;+ + // Distances along the axis parallel to n+ cpFloat d_e1_a = cpvcross(e1.a.p, n);+ cpFloat d_e1_b = cpvcross(e1.b.p, n);+ cpFloat d_e2_a = cpvcross(e2.a.p, n);+ cpFloat d_e2_b = cpvcross(e2.b.p, n);+ + // TODO + min isn't a complete fix.+ cpFloat e1_denom = 1.0f/(d_e1_b - d_e1_a + CPFLOAT_MIN);+ cpFloat e2_denom = 1.0f/(d_e2_b - d_e2_a + CPFLOAT_MIN);+ + // Project the endpoints of the two edges onto the opposing edge, clamping them as necessary.+ // Compare the projected points to the collision normal to see if the shapes overlap there.+ {+ cpVect p1 = cpvadd(cpvmult(n, e1.r), cpvlerp(e1.a.p, e1.b.p, cpfclamp01((d_e2_b - d_e1_a)*e1_denom)));+ cpVect p2 = cpvadd(cpvmult(n, -e2.r), cpvlerp(e2.a.p, e2.b.p, cpfclamp01((d_e1_a - d_e2_a)*e2_denom)));+ cpFloat dist = cpvdot(cpvsub(p2, p1), n);+ if(dist <= 0.0f){+ cpHashValue hash_1a2b = CP_HASH_PAIR(e1.a.hash, e2.b.hash);+ cpCollisionInfoPushContact(info, p1, p2, hash_1a2b);+ }+ }{+ cpVect p1 = cpvadd(cpvmult(n, e1.r), cpvlerp(e1.a.p, e1.b.p, cpfclamp01((d_e2_a - d_e1_a)*e1_denom)));+ cpVect p2 = cpvadd(cpvmult(n, -e2.r), cpvlerp(e2.a.p, e2.b.p, cpfclamp01((d_e1_b - d_e2_a)*e2_denom)));+ cpFloat dist = cpvdot(cpvsub(p2, p1), n);+ if(dist <= 0.0f){+ cpHashValue hash_1b2a = CP_HASH_PAIR(e1.b.hash, e2.a.hash);+ cpCollisionInfoPushContact(info, p1, p2, hash_1b2a);+ }+ }+ }+}++//MARK: Collision Functions++typedef void (*CollisionFunc)(const cpShape *a, const cpShape *b, struct cpCollisionInfo *info);++// Collide circle shapes.+static void+CircleToCircle(const cpCircleShape *c1, const cpCircleShape *c2, struct cpCollisionInfo *info)+{+ cpFloat mindist = c1->r + c2->r;+ cpVect delta = cpvsub(c2->tc, c1->tc);+ cpFloat distsq = cpvlengthsq(delta);+ + if(distsq < mindist*mindist){+ cpFloat dist = cpfsqrt(distsq);+ cpVect n = info->n = (dist ? cpvmult(delta, 1.0f/dist) : cpv(1.0f, 0.0f));+ cpCollisionInfoPushContact(info, cpvadd(c1->tc, cpvmult(n, c1->r)), cpvadd(c2->tc, cpvmult(n, -c2->r)), 0);+ }+}++static void+CircleToSegment(const cpCircleShape *circle, const cpSegmentShape *segment, struct cpCollisionInfo *info)+{+ cpVect seg_a = segment->ta;+ cpVect seg_b = segment->tb;+ cpVect center = circle->tc;+ + // Find the closest point on the segment to the circle.+ cpVect seg_delta = cpvsub(seg_b, seg_a);+ cpFloat closest_t = cpfclamp01(cpvdot(seg_delta, cpvsub(center, seg_a))/cpvlengthsq(seg_delta));+ cpVect closest = cpvadd(seg_a, cpvmult(seg_delta, closest_t));+ + // Compare the radii of the two shapes to see if they are colliding.+ cpFloat mindist = circle->r + segment->r;+ cpVect delta = cpvsub(closest, center);+ cpFloat distsq = cpvlengthsq(delta);+ if(distsq < mindist*mindist){+ cpFloat dist = cpfsqrt(distsq);+ // Handle coincident shapes as gracefully as possible.+ cpVect n = info->n = (dist ? cpvmult(delta, 1.0f/dist) : segment->tn);+ + // Reject endcap collisions if tangents are provided.+ cpVect rot = cpBodyGetRotation(segment->shape.body);+ if(+ (closest_t != 0.0f || cpvdot(n, cpvrotate(segment->a_tangent, rot)) >= 0.0) &&+ (closest_t != 1.0f || cpvdot(n, cpvrotate(segment->b_tangent, rot)) >= 0.0)+ ){+ cpCollisionInfoPushContact(info, cpvadd(center, cpvmult(n, circle->r)), cpvadd(closest, cpvmult(n, -segment->r)), 0);+ }+ }+}++static void+SegmentToSegment(const cpSegmentShape *seg1, const cpSegmentShape *seg2, struct cpCollisionInfo *info)+{+ struct SupportContext context = {(cpShape *)seg1, (cpShape *)seg2, (SupportPointFunc)SegmentSupportPoint, (SupportPointFunc)SegmentSupportPoint};+ struct ClosestPoints points = GJK(&context, &info->id);+ +#if DRAW_CLOSEST+#if PRINT_LOG+// ChipmunkDemoPrintString("Distance: %.2f\n", points.d);+#endif+ + ChipmunkDebugDrawDot(6.0, points.a, RGBAColor(1, 1, 1, 1));+ ChipmunkDebugDrawDot(6.0, points.b, RGBAColor(1, 1, 1, 1));+ ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));+ ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));+#endif+ + cpVect n = points.n;+ cpVect rot1 = cpBodyGetRotation(seg1->shape.body);+ cpVect rot2 = cpBodyGetRotation(seg2->shape.body);+ + // If the closest points are nearer than the sum of the radii...+ if(+ points.d <= (seg1->r + seg2->r) && (+ // Reject endcap collisions if tangents are provided.+ (!cpveql(points.a, seg1->ta) || cpvdot(n, cpvrotate(seg1->a_tangent, rot1)) <= 0.0) &&+ (!cpveql(points.a, seg1->tb) || cpvdot(n, cpvrotate(seg1->b_tangent, rot1)) <= 0.0) &&+ (!cpveql(points.b, seg2->ta) || cpvdot(n, cpvrotate(seg2->a_tangent, rot2)) >= 0.0) &&+ (!cpveql(points.b, seg2->tb) || cpvdot(n, cpvrotate(seg2->b_tangent, rot2)) >= 0.0)+ )+ ){+ ContactPoints(SupportEdgeForSegment(seg1, n), SupportEdgeForSegment(seg2, cpvneg(n)), points, info);+ }+}++static void+PolyToPoly(const cpPolyShape *poly1, const cpPolyShape *poly2, struct cpCollisionInfo *info)+{+ struct SupportContext context = {(cpShape *)poly1, (cpShape *)poly2, (SupportPointFunc)PolySupportPoint, (SupportPointFunc)PolySupportPoint};+ struct ClosestPoints points = GJK(&context, &info->id);+ +#if DRAW_CLOSEST+#if PRINT_LOG+// ChipmunkDemoPrintString("Distance: %.2f\n", points.d);+#endif+ + ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1));+ ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1));+ ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));+ ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));+#endif+ + // If the closest points are nearer than the sum of the radii...+ if(points.d - poly1->r - poly2->r <= 0.0){+ ContactPoints(SupportEdgeForPoly(poly1, points.n), SupportEdgeForPoly(poly2, cpvneg(points.n)), points, info);+ }+}++static void+SegmentToPoly(const cpSegmentShape *seg, const cpPolyShape *poly, struct cpCollisionInfo *info)+{+ struct SupportContext context = {(cpShape *)seg, (cpShape *)poly, (SupportPointFunc)SegmentSupportPoint, (SupportPointFunc)PolySupportPoint};+ struct ClosestPoints points = GJK(&context, &info->id);+ +#if DRAW_CLOSEST+#if PRINT_LOG+// ChipmunkDemoPrintString("Distance: %.2f\n", points.d);+#endif+ + ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1));+ ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1));+ ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));+ ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));+#endif+ + cpVect n = points.n;+ cpVect rot = cpBodyGetRotation(seg->shape.body);+ + if(+ // If the closest points are nearer than the sum of the radii...+ points.d - seg->r - poly->r <= 0.0 && (+ // Reject endcap collisions if tangents are provided.+ (!cpveql(points.a, seg->ta) || cpvdot(n, cpvrotate(seg->a_tangent, rot)) <= 0.0) &&+ (!cpveql(points.a, seg->tb) || cpvdot(n, cpvrotate(seg->b_tangent, rot)) <= 0.0)+ )+ ){+ ContactPoints(SupportEdgeForSegment(seg, n), SupportEdgeForPoly(poly, cpvneg(n)), points, info);+ }+}++static void+CircleToPoly(const cpCircleShape *circle, const cpPolyShape *poly, struct cpCollisionInfo *info)+{+ struct SupportContext context = {(cpShape *)circle, (cpShape *)poly, (SupportPointFunc)CircleSupportPoint, (SupportPointFunc)PolySupportPoint};+ struct ClosestPoints points = GJK(&context, &info->id);+ +#if DRAW_CLOSEST+ ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1));+ ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1));+ ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));+ ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));+#endif+ + // If the closest points are nearer than the sum of the radii...+ if(points.d <= circle->r + poly->r){+ cpVect n = info->n = points.n;+ cpCollisionInfoPushContact(info, cpvadd(points.a, cpvmult(n, circle->r)), cpvadd(points.b, cpvmult(n, poly->r)), 0);+ }+}++static void+CollisionError(const cpShape *circle, const cpShape *poly, struct cpCollisionInfo *info)+{+ cpAssertHard(cpFalse, "Internal Error: Shape types are not sorted.");+}+++static const CollisionFunc BuiltinCollisionFuncs[9] = {+ (CollisionFunc)CircleToCircle,+ CollisionError,+ CollisionError,+ (CollisionFunc)CircleToSegment,+ (CollisionFunc)SegmentToSegment,+ CollisionError,+ (CollisionFunc)CircleToPoly,+ (CollisionFunc)SegmentToPoly,+ (CollisionFunc)PolyToPoly,+};+static const CollisionFunc *CollisionFuncs = BuiltinCollisionFuncs;++struct cpCollisionInfo+cpCollide(const cpShape *a, const cpShape *b, cpCollisionID id, struct cpContact *contacts)+{+ struct cpCollisionInfo info = {a, b, id, cpvzero, 0, contacts};+ + // Make sure the shape types are in order.+ if(a->klass->type > b->klass->type){+ info.a = b;+ info.b = a;+ }+ + CollisionFuncs[info.a->klass->type + info.b->klass->type*CP_NUM_SHAPES](info.a, info.b, &info);+ +// if(0){+// for(int i=0; i<info.count; i++){+// cpVect r1 = info.arr[i].r1;+// cpVect r2 = info.arr[i].r2;+// cpVect mid = cpvlerp(r1, r2, 0.5f);+// +// ChipmunkDebugDrawSegment(r1, mid, RGBAColor(1, 0, 0, 1));+// ChipmunkDebugDrawSegment(r2, mid, RGBAColor(0, 0, 1, 1));+// }+// }+ + return info;+}
+ Chipmunk2D-7.0.2/src/cpConstraint.c view
@@ -0,0 +1,173 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++// TODO: Comment me!++void cpConstraintDestroy(cpConstraint *constraint){}++void+cpConstraintFree(cpConstraint *constraint)+{+ if(constraint){+ cpConstraintDestroy(constraint);+ cpfree(constraint);+ }+}++void+cpConstraintInit(cpConstraint *constraint, const cpConstraintClass *klass, cpBody *a, cpBody *b)+{+ constraint->klass = klass;+ + constraint->a = a;+ constraint->b = b;+ constraint->space = NULL;+ + constraint->next_a = NULL;+ constraint->next_b = NULL;+ + constraint->maxForce = (cpFloat)INFINITY;+ constraint->errorBias = cpfpow(1.0f - 0.1f, 60.0f);+ constraint->maxBias = (cpFloat)INFINITY;+ + constraint->collideBodies = cpTrue;+ + constraint->preSolve = NULL;+ constraint->postSolve = NULL;+}++cpSpace *+cpConstraintGetSpace(const cpConstraint *constraint)+{+ return constraint->space;+}++cpBody *+cpConstraintGetBodyA(const cpConstraint *constraint)+{+ return constraint->a;+}++cpBody *+cpConstraintGetBodyB(const cpConstraint *constraint)+{+ return constraint->b;+}++cpFloat+cpConstraintGetMaxForce(const cpConstraint *constraint)+{+ return constraint->maxForce;+}++void+cpConstraintSetMaxForce(cpConstraint *constraint, cpFloat maxForce)+{+ cpAssertHard(maxForce >= 0.0f, "maxForce must be positive.");+ cpConstraintActivateBodies(constraint);+ constraint->maxForce = maxForce;+}++cpFloat+cpConstraintGetErrorBias(const cpConstraint *constraint)+{+ return constraint->errorBias;+}++void+cpConstraintSetErrorBias(cpConstraint *constraint, cpFloat errorBias)+{+ cpAssertHard(errorBias >= 0.0f, "errorBias must be positive.");+ cpConstraintActivateBodies(constraint);+ constraint->errorBias = errorBias;+}++cpFloat+cpConstraintGetMaxBias(const cpConstraint *constraint)+{+ return constraint->maxBias;+}++void+cpConstraintSetMaxBias(cpConstraint *constraint, cpFloat maxBias)+{+ cpAssertHard(maxBias >= 0.0f, "maxBias must be positive.");+ cpConstraintActivateBodies(constraint);+ constraint->maxBias = maxBias;+}++cpBool+cpConstraintGetCollideBodies(const cpConstraint *constraint)+{+ return constraint->collideBodies;+}++void+cpConstraintSetCollideBodies(cpConstraint *constraint, cpBool collideBodies)+{+ cpConstraintActivateBodies(constraint);+ constraint->collideBodies = collideBodies;+}++cpConstraintPreSolveFunc+cpConstraintGetPreSolveFunc(const cpConstraint *constraint)+{+ return constraint->preSolve;+}++void+cpConstraintSetPreSolveFunc(cpConstraint *constraint, cpConstraintPreSolveFunc preSolveFunc)+{+ constraint->preSolve = preSolveFunc;+}++cpConstraintPostSolveFunc+cpConstraintGetPostSolveFunc(const cpConstraint *constraint)+{+ return constraint->postSolve;+}++void+cpConstraintSetPostSolveFunc(cpConstraint *constraint, cpConstraintPostSolveFunc postSolveFunc)+{+ constraint->postSolve = postSolveFunc;+}++cpDataPointer+cpConstraintGetUserData(const cpConstraint *constraint)+{+ return constraint->userData;+}++void+cpConstraintSetUserData(cpConstraint *constraint, cpDataPointer userData)+{+ constraint->userData = userData;+}+++cpFloat+cpConstraintGetImpulse(cpConstraint *constraint)+{+ return constraint->klass->getImpulse(constraint);+}
+ Chipmunk2D-7.0.2/src/cpDampedRotarySpring.c view
@@ -0,0 +1,178 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++static cpFloat+defaultSpringTorque(cpDampedRotarySpring *spring, cpFloat relativeAngle){+ return (relativeAngle - spring->restAngle)*spring->stiffness;+}++static void+preStep(cpDampedRotarySpring *spring, cpFloat dt)+{+ cpBody *a = spring->constraint.a;+ cpBody *b = spring->constraint.b;+ + cpFloat moment = a->i_inv + b->i_inv;+ cpAssertSoft(moment != 0.0, "Unsolvable spring.");+ spring->iSum = 1.0f/moment;++ spring->w_coef = 1.0f - cpfexp(-spring->damping*dt*moment);+ spring->target_wrn = 0.0f;++ // apply spring torque+ cpFloat j_spring = spring->springTorqueFunc((cpConstraint *)spring, a->a - b->a)*dt;+ spring->jAcc = j_spring;+ + a->w -= j_spring*a->i_inv;+ b->w += j_spring*b->i_inv;+}++static void applyCachedImpulse(cpDampedRotarySpring *spring, cpFloat dt_coef){}++static void+applyImpulse(cpDampedRotarySpring *spring, cpFloat dt)+{+ cpBody *a = spring->constraint.a;+ cpBody *b = spring->constraint.b;+ + // compute relative velocity+ cpFloat wrn = a->w - b->w;//normal_relative_velocity(a, b, r1, r2, n) - spring->target_vrn;+ + // compute velocity loss from drag+ // not 100% certain this is derived correctly, though it makes sense+ cpFloat w_damp = (spring->target_wrn - wrn)*spring->w_coef;+ spring->target_wrn = wrn + w_damp;+ + //apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, v_damp*spring->nMass));+ cpFloat j_damp = w_damp*spring->iSum;+ spring->jAcc += j_damp;+ + a->w += j_damp*a->i_inv;+ b->w -= j_damp*b->i_inv;+}++static cpFloat+getImpulse(cpDampedRotarySpring *spring)+{+ return spring->jAcc;+}++static const cpConstraintClass klass = {+ (cpConstraintPreStepImpl)preStep,+ (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,+ (cpConstraintApplyImpulseImpl)applyImpulse,+ (cpConstraintGetImpulseImpl)getImpulse,+};++cpDampedRotarySpring *+cpDampedRotarySpringAlloc(void)+{+ return (cpDampedRotarySpring *)cpcalloc(1, sizeof(cpDampedRotarySpring));+}++cpDampedRotarySpring *+cpDampedRotarySpringInit(cpDampedRotarySpring *spring, cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping)+{+ cpConstraintInit((cpConstraint *)spring, &klass, a, b);+ + spring->restAngle = restAngle;+ spring->stiffness = stiffness;+ spring->damping = damping;+ spring->springTorqueFunc = (cpDampedRotarySpringTorqueFunc)defaultSpringTorque;+ + spring->jAcc = 0.0f;+ + return spring;+}++cpConstraint *+cpDampedRotarySpringNew(cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping)+{+ return (cpConstraint *)cpDampedRotarySpringInit(cpDampedRotarySpringAlloc(), a, b, restAngle, stiffness, damping);+}++cpBool+cpConstraintIsDampedRotarySpring(const cpConstraint *constraint)+{+ return (constraint->klass == &klass);+}++cpFloat+cpDampedRotarySpringGetRestAngle(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");+ return ((cpDampedRotarySpring *)constraint)->restAngle;+}++void+cpDampedRotarySpringSetRestAngle(cpConstraint *constraint, cpFloat restAngle)+{+ cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");+ cpConstraintActivateBodies(constraint);+ ((cpDampedRotarySpring *)constraint)->restAngle = restAngle;+}++cpFloat+cpDampedRotarySpringGetStiffness(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");+ return ((cpDampedRotarySpring *)constraint)->stiffness;+}++void+cpDampedRotarySpringSetStiffness(cpConstraint *constraint, cpFloat stiffness)+{+ cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");+ cpConstraintActivateBodies(constraint);+ ((cpDampedRotarySpring *)constraint)->stiffness = stiffness;+}++cpFloat+cpDampedRotarySpringGetDamping(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");+ return ((cpDampedRotarySpring *)constraint)->damping;+}++void+cpDampedRotarySpringSetDamping(cpConstraint *constraint, cpFloat damping)+{+ cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");+ cpConstraintActivateBodies(constraint);+ ((cpDampedRotarySpring *)constraint)->damping = damping;+}++cpDampedRotarySpringTorqueFunc+cpDampedRotarySpringGetSpringTorqueFunc(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");+ return ((cpDampedRotarySpring *)constraint)->springTorqueFunc;+}++void+cpDampedRotarySpringSetSpringTorqueFunc(cpConstraint *constraint, cpDampedRotarySpringTorqueFunc springTorqueFunc)+{+ cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");+ cpConstraintActivateBodies(constraint);+ ((cpDampedRotarySpring *)constraint)->springTorqueFunc = springTorqueFunc;+}
+ Chipmunk2D-7.0.2/src/cpDampedSpring.c view
@@ -0,0 +1,216 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++static cpFloat+defaultSpringForce(cpDampedSpring *spring, cpFloat dist){+ return (spring->restLength - dist)*spring->stiffness;+}++static void+preStep(cpDampedSpring *spring, cpFloat dt)+{+ cpBody *a = spring->constraint.a;+ cpBody *b = spring->constraint.b;+ + spring->r1 = cpTransformVect(a->transform, cpvsub(spring->anchorA, a->cog));+ spring->r2 = cpTransformVect(b->transform, cpvsub(spring->anchorB, b->cog));+ + cpVect delta = cpvsub(cpvadd(b->p, spring->r2), cpvadd(a->p, spring->r1));+ cpFloat dist = cpvlength(delta);+ spring->n = cpvmult(delta, 1.0f/(dist ? dist : INFINITY));+ + cpFloat k = k_scalar(a, b, spring->r1, spring->r2, spring->n);+ cpAssertSoft(k != 0.0, "Unsolvable spring.");+ spring->nMass = 1.0f/k;+ + spring->target_vrn = 0.0f;+ spring->v_coef = 1.0f - cpfexp(-spring->damping*dt*k);++ // apply spring force+ cpFloat f_spring = spring->springForceFunc((cpConstraint *)spring, dist);+ cpFloat j_spring = spring->jAcc = f_spring*dt;+ apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, j_spring));+}++static void applyCachedImpulse(cpDampedSpring *spring, cpFloat dt_coef){}++static void+applyImpulse(cpDampedSpring *spring, cpFloat dt)+{+ cpBody *a = spring->constraint.a;+ cpBody *b = spring->constraint.b;+ + cpVect n = spring->n;+ cpVect r1 = spring->r1;+ cpVect r2 = spring->r2;++ // compute relative velocity+ cpFloat vrn = normal_relative_velocity(a, b, r1, r2, n);+ + // compute velocity loss from drag+ cpFloat v_damp = (spring->target_vrn - vrn)*spring->v_coef;+ spring->target_vrn = vrn + v_damp;+ + cpFloat j_damp = v_damp*spring->nMass;+ spring->jAcc += j_damp;+ apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, j_damp));+}++static cpFloat+getImpulse(cpDampedSpring *spring)+{+ return spring->jAcc;+}++static const cpConstraintClass klass = {+ (cpConstraintPreStepImpl)preStep,+ (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,+ (cpConstraintApplyImpulseImpl)applyImpulse,+ (cpConstraintGetImpulseImpl)getImpulse,+};++cpDampedSpring *+cpDampedSpringAlloc(void)+{+ return (cpDampedSpring *)cpcalloc(1, sizeof(cpDampedSpring));+}++cpDampedSpring *+cpDampedSpringInit(cpDampedSpring *spring, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping)+{+ cpConstraintInit((cpConstraint *)spring, &klass, a, b);+ + spring->anchorA = anchorA;+ spring->anchorB = anchorB;+ + spring->restLength = restLength;+ spring->stiffness = stiffness;+ spring->damping = damping;+ spring->springForceFunc = (cpDampedSpringForceFunc)defaultSpringForce;+ + spring->jAcc = 0.0f;+ + return spring;+}++cpConstraint *+cpDampedSpringNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping)+{+ return (cpConstraint *)cpDampedSpringInit(cpDampedSpringAlloc(), a, b, anchorA, anchorB, restLength, stiffness, damping);+}++cpBool+cpConstraintIsDampedSpring(const cpConstraint *constraint)+{+ return (constraint->klass == &klass);+}++cpVect+cpDampedSpringGetAnchorA(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");+ return ((cpDampedSpring *)constraint)->anchorA;+}++void+cpDampedSpringSetAnchorA(cpConstraint *constraint, cpVect anchorA)+{+ cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");+ cpConstraintActivateBodies(constraint);+ ((cpDampedSpring *)constraint)->anchorA = anchorA;+}++cpVect+cpDampedSpringGetAnchorB(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");+ return ((cpDampedSpring *)constraint)->anchorB;+}++void+cpDampedSpringSetAnchorB(cpConstraint *constraint, cpVect anchorB)+{+ cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");+ cpConstraintActivateBodies(constraint);+ ((cpDampedSpring *)constraint)->anchorB = anchorB;+}++cpFloat+cpDampedSpringGetRestLength(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");+ return ((cpDampedSpring *)constraint)->restLength;+}++void+cpDampedSpringSetRestLength(cpConstraint *constraint, cpFloat restLength)+{+ cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");+ cpConstraintActivateBodies(constraint);+ ((cpDampedSpring *)constraint)->restLength = restLength;+}++cpFloat+cpDampedSpringGetStiffness(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");+ return ((cpDampedSpring *)constraint)->stiffness;+}++void+cpDampedSpringSetStiffness(cpConstraint *constraint, cpFloat stiffness)+{+ cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");+ cpConstraintActivateBodies(constraint);+ ((cpDampedSpring *)constraint)->stiffness = stiffness;+}++cpFloat+cpDampedSpringGetDamping(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");+ return ((cpDampedSpring *)constraint)->damping;+}++void+cpDampedSpringSetDamping(cpConstraint *constraint, cpFloat damping)+{+ cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");+ cpConstraintActivateBodies(constraint);+ ((cpDampedSpring *)constraint)->damping = damping;+}++cpDampedSpringForceFunc+cpDampedSpringGetSpringForceFunc(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");+ return ((cpDampedSpring *)constraint)->springForceFunc;+}++void+cpDampedSpringSetSpringForceFunc(cpConstraint *constraint, cpDampedSpringForceFunc springForceFunc)+{+ cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");+ cpConstraintActivateBodies(constraint);+ ((cpDampedSpring *)constraint)->springForceFunc = springForceFunc;+}
+ Chipmunk2D-7.0.2/src/cpGearJoint.c view
@@ -0,0 +1,145 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++static void+preStep(cpGearJoint *joint, cpFloat dt)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + // calculate moment of inertia coefficient.+ joint->iSum = 1.0f/(a->i_inv*joint->ratio_inv + joint->ratio*b->i_inv);+ + // calculate bias velocity+ cpFloat maxBias = joint->constraint.maxBias;+ joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*(b->a*joint->ratio - a->a - joint->phase)/dt, -maxBias, maxBias);+}++static void+applyCachedImpulse(cpGearJoint *joint, cpFloat dt_coef)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + cpFloat j = joint->jAcc*dt_coef;+ a->w -= j*a->i_inv*joint->ratio_inv;+ b->w += j*b->i_inv;+}++static void+applyImpulse(cpGearJoint *joint, cpFloat dt)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + // compute relative rotational velocity+ cpFloat wr = b->w*joint->ratio - a->w;+ + cpFloat jMax = joint->constraint.maxForce*dt;+ + // compute normal impulse + cpFloat j = (joint->bias - wr)*joint->iSum;+ cpFloat jOld = joint->jAcc;+ joint->jAcc = cpfclamp(jOld + j, -jMax, jMax);+ j = joint->jAcc - jOld;+ + // apply impulse+ a->w -= j*a->i_inv*joint->ratio_inv;+ b->w += j*b->i_inv;+}++static cpFloat+getImpulse(cpGearJoint *joint)+{+ return cpfabs(joint->jAcc);+}++static const cpConstraintClass klass = {+ (cpConstraintPreStepImpl)preStep,+ (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,+ (cpConstraintApplyImpulseImpl)applyImpulse,+ (cpConstraintGetImpulseImpl)getImpulse,+};++cpGearJoint *+cpGearJointAlloc(void)+{+ return (cpGearJoint *)cpcalloc(1, sizeof(cpGearJoint));+}++cpGearJoint *+cpGearJointInit(cpGearJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio)+{+ cpConstraintInit((cpConstraint *)joint, &klass, a, b);+ + joint->phase = phase;+ joint->ratio = ratio;+ joint->ratio_inv = 1.0f/ratio;+ + joint->jAcc = 0.0f;+ + return joint;+}++cpConstraint *+cpGearJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio)+{+ return (cpConstraint *)cpGearJointInit(cpGearJointAlloc(), a, b, phase, ratio);+}++cpBool+cpConstraintIsGearJoint(const cpConstraint *constraint)+{+ return (constraint->klass == &klass);+}++cpFloat+cpGearJointGetPhase(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint.");+ return ((cpGearJoint *)constraint)->phase;+}++void+cpGearJointSetPhase(cpConstraint *constraint, cpFloat phase)+{+ cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint.");+ cpConstraintActivateBodies(constraint);+ ((cpGearJoint *)constraint)->phase = phase;+}++cpFloat+cpGearJointGetRatio(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint.");+ return ((cpGearJoint *)constraint)->ratio;+}++void+cpGearJointSetRatio(cpConstraint *constraint, cpFloat ratio)+{+ cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint.");+ cpConstraintActivateBodies(constraint);+ ((cpGearJoint *)constraint)->ratio = ratio;+ ((cpGearJoint *)constraint)->ratio_inv = 1.0f/ratio;+}
+ Chipmunk2D-7.0.2/src/cpGrooveJoint.c view
@@ -0,0 +1,197 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++static void+preStep(cpGrooveJoint *joint, cpFloat dt)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + // calculate endpoints in worldspace+ cpVect ta = cpTransformPoint(a->transform, joint->grv_a);+ cpVect tb = cpTransformPoint(a->transform, joint->grv_b);++ // calculate axis+ cpVect n = cpTransformVect(a->transform, joint->grv_n);+ cpFloat d = cpvdot(ta, n);+ + joint->grv_tn = n;+ joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog));+ + // calculate tangential distance along the axis of r2+ cpFloat td = cpvcross(cpvadd(b->p, joint->r2), n);+ // calculate clamping factor and r2+ if(td <= cpvcross(ta, n)){+ joint->clamp = 1.0f;+ joint->r1 = cpvsub(ta, a->p);+ } else if(td >= cpvcross(tb, n)){+ joint->clamp = -1.0f;+ joint->r1 = cpvsub(tb, a->p);+ } else {+ joint->clamp = 0.0f;+ joint->r1 = cpvsub(cpvadd(cpvmult(cpvperp(n), -td), cpvmult(n, d)), a->p);+ }+ + // Calculate mass tensor+ joint->k = k_tensor(a, b, joint->r1, joint->r2);+ + // calculate bias velocity+ cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1));+ joint->bias = cpvclamp(cpvmult(delta, -bias_coef(joint->constraint.errorBias, dt)/dt), joint->constraint.maxBias);+}++static void+applyCachedImpulse(cpGrooveJoint *joint, cpFloat dt_coef)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + apply_impulses(a, b, joint->r1, joint->r2, cpvmult(joint->jAcc, dt_coef));+}++static inline cpVect+grooveConstrain(cpGrooveJoint *joint, cpVect j, cpFloat dt){+ cpVect n = joint->grv_tn;+ cpVect jClamp = (joint->clamp*cpvcross(j, n) > 0.0f) ? j : cpvproject(j, n);+ return cpvclamp(jClamp, joint->constraint.maxForce*dt);+}++static void+applyImpulse(cpGrooveJoint *joint, cpFloat dt)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + cpVect r1 = joint->r1;+ cpVect r2 = joint->r2;+ + // compute impulse+ cpVect vr = relative_velocity(a, b, r1, r2);++ cpVect j = cpMat2x2Transform(joint->k, cpvsub(joint->bias, vr));+ cpVect jOld = joint->jAcc;+ joint->jAcc = grooveConstrain(joint, cpvadd(jOld, j), dt);+ j = cpvsub(joint->jAcc, jOld);+ + // apply impulse+ apply_impulses(a, b, joint->r1, joint->r2, j);+}++static cpFloat+getImpulse(cpGrooveJoint *joint)+{+ return cpvlength(joint->jAcc);+}++static const cpConstraintClass klass = {+ (cpConstraintPreStepImpl)preStep,+ (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,+ (cpConstraintApplyImpulseImpl)applyImpulse,+ (cpConstraintGetImpulseImpl)getImpulse,+};++cpGrooveJoint *+cpGrooveJointAlloc(void)+{+ return (cpGrooveJoint *)cpcalloc(1, sizeof(cpGrooveJoint));+}++cpGrooveJoint *+cpGrooveJointInit(cpGrooveJoint *joint, cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB)+{+ cpConstraintInit((cpConstraint *)joint, &klass, a, b);+ + joint->grv_a = groove_a;+ joint->grv_b = groove_b;+ joint->grv_n = cpvperp(cpvnormalize(cpvsub(groove_b, groove_a)));+ joint->anchorB = anchorB;+ + joint->jAcc = cpvzero;+ + return joint;+}++cpConstraint *+cpGrooveJointNew(cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB)+{+ return (cpConstraint *)cpGrooveJointInit(cpGrooveJointAlloc(), a, b, groove_a, groove_b, anchorB);+}++cpBool+cpConstraintIsGrooveJoint(const cpConstraint *constraint)+{+ return (constraint->klass == &klass);+}++cpVect+cpGrooveJointGetGrooveA(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint.");+ return ((cpGrooveJoint *)constraint)->grv_a;+}++void+cpGrooveJointSetGrooveA(cpConstraint *constraint, cpVect value)+{+ cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint.");+ cpGrooveJoint *g = (cpGrooveJoint *)constraint;+ + g->grv_a = value;+ g->grv_n = cpvperp(cpvnormalize(cpvsub(g->grv_b, value)));+ + cpConstraintActivateBodies(constraint);+}++cpVect+cpGrooveJointGetGrooveB(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint.");+ return ((cpGrooveJoint *)constraint)->grv_b;+}++void+cpGrooveJointSetGrooveB(cpConstraint *constraint, cpVect value)+{+ cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint.");+ cpGrooveJoint *g = (cpGrooveJoint *)constraint;+ + g->grv_b = value;+ g->grv_n = cpvperp(cpvnormalize(cpvsub(value, g->grv_a)));+ + cpConstraintActivateBodies(constraint);+}++cpVect+cpGrooveJointGetAnchorB(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint.");+ return ((cpGrooveJoint *)constraint)->anchorB;+}++void+cpGrooveJointSetAnchorB(cpConstraint *constraint, cpVect anchorB)+{+ cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint.");+ cpConstraintActivateBodies(constraint);+ ((cpGrooveJoint *)constraint)->anchorB = anchorB;+}
+ Chipmunk2D-7.0.2/src/cpHashSet.c view
@@ -0,0 +1,253 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"+#include "prime.h"++typedef struct cpHashSetBin {+ void *elt;+ cpHashValue hash;+ struct cpHashSetBin *next;+} cpHashSetBin;++struct cpHashSet {+ unsigned int entries, size;+ + cpHashSetEqlFunc eql;+ void *default_value;+ + cpHashSetBin **table;+ cpHashSetBin *pooledBins;+ + cpArray *allocatedBuffers;+};++void+cpHashSetFree(cpHashSet *set)+{+ if(set){+ cpfree(set->table);+ + cpArrayFreeEach(set->allocatedBuffers, cpfree);+ cpArrayFree(set->allocatedBuffers);+ + cpfree(set);+ }+}++cpHashSet *+cpHashSetNew(int size, cpHashSetEqlFunc eqlFunc)+{+ cpHashSet *set = (cpHashSet *)cpcalloc(1, sizeof(cpHashSet));+ + set->size = next_prime(size);+ set->entries = 0;+ + set->eql = eqlFunc;+ set->default_value = NULL;+ + set->table = (cpHashSetBin **)cpcalloc(set->size, sizeof(cpHashSetBin *));+ set->pooledBins = NULL;+ + set->allocatedBuffers = cpArrayNew(0);+ + return set;+}++void+cpHashSetSetDefaultValue(cpHashSet *set, void *default_value)+{+ set->default_value = default_value;+}++static int+setIsFull(cpHashSet *set)+{+ return (set->entries >= set->size);+}++static void+cpHashSetResize(cpHashSet *set)+{+ // Get the next approximate doubled prime.+ unsigned int newSize = next_prime(set->size + 1);+ // Allocate a new table.+ cpHashSetBin **newTable = (cpHashSetBin **)cpcalloc(newSize, sizeof(cpHashSetBin *));+ + // Iterate over the chains.+ for(unsigned int i=0; i<set->size; i++){+ // Rehash the bins into the new table.+ cpHashSetBin *bin = set->table[i];+ while(bin){+ cpHashSetBin *next = bin->next;+ + cpHashValue idx = bin->hash%newSize;+ bin->next = newTable[idx];+ newTable[idx] = bin;+ + bin = next;+ }+ }+ + cpfree(set->table);+ + set->table = newTable;+ set->size = newSize;+}++static inline void+recycleBin(cpHashSet *set, cpHashSetBin *bin)+{+ bin->next = set->pooledBins;+ set->pooledBins = bin;+ bin->elt = NULL;+}++static cpHashSetBin *+getUnusedBin(cpHashSet *set)+{+ cpHashSetBin *bin = set->pooledBins;+ + if(bin){+ set->pooledBins = bin->next;+ return bin;+ } else {+ // Pool is exhausted, make more+ int count = CP_BUFFER_BYTES/sizeof(cpHashSetBin);+ cpAssertHard(count, "Internal Error: Buffer size is too small.");+ + cpHashSetBin *buffer = (cpHashSetBin *)cpcalloc(1, CP_BUFFER_BYTES);+ cpArrayPush(set->allocatedBuffers, buffer);+ + // push all but the first one, return it instead+ for(int i=1; i<count; i++) recycleBin(set, buffer + i);+ return buffer;+ }+}++int+cpHashSetCount(cpHashSet *set)+{+ return set->entries;+}++void *+cpHashSetInsert(cpHashSet *set, cpHashValue hash, void *ptr, cpHashSetTransFunc trans, void *data)+{+ cpHashValue idx = hash%set->size;+ + // Find the bin with the matching element.+ cpHashSetBin *bin = set->table[idx];+ while(bin && !set->eql(ptr, bin->elt))+ bin = bin->next;+ + // Create it if necessary.+ if(!bin){+ bin = getUnusedBin(set);+ bin->hash = hash;+ bin->elt = (trans ? trans(ptr, data) : data);+ + bin->next = set->table[idx];+ set->table[idx] = bin;+ + set->entries++;+ if(setIsFull(set)) cpHashSetResize(set);+ }+ + return bin->elt;+}++void *+cpHashSetRemove(cpHashSet *set, cpHashValue hash, void *ptr)+{+ cpHashValue idx = hash%set->size;+ + cpHashSetBin **prev_ptr = &set->table[idx];+ cpHashSetBin *bin = set->table[idx];+ + // Find the bin+ while(bin && !set->eql(ptr, bin->elt)){+ prev_ptr = &bin->next;+ bin = bin->next;+ }+ + // Remove it if it exists.+ if(bin){+ // Update the previous linked list pointer+ (*prev_ptr) = bin->next;+ set->entries--;+ + void *elt = bin->elt;+ recycleBin(set, bin);+ + return elt;+ }+ + return NULL;+}++void *+cpHashSetFind(cpHashSet *set, cpHashValue hash, void *ptr)+{ + cpHashValue idx = hash%set->size;+ cpHashSetBin *bin = set->table[idx];+ while(bin && !set->eql(ptr, bin->elt))+ bin = bin->next;+ + return (bin ? bin->elt : set->default_value);+}++void+cpHashSetEach(cpHashSet *set, cpHashSetIteratorFunc func, void *data)+{+ for(unsigned int i=0; i<set->size; i++){+ cpHashSetBin *bin = set->table[i];+ while(bin){+ cpHashSetBin *next = bin->next;+ func(bin->elt, data);+ bin = next;+ }+ }+}++void+cpHashSetFilter(cpHashSet *set, cpHashSetFilterFunc func, void *data)+{+ for(unsigned int i=0; i<set->size; i++){+ // The rest works similarly to cpHashSetRemove() above.+ cpHashSetBin **prev_ptr = &set->table[i];+ cpHashSetBin *bin = set->table[i];+ while(bin){+ cpHashSetBin *next = bin->next;+ + if(func(bin->elt, data)){+ prev_ptr = &bin->next;+ } else {+ (*prev_ptr) = next;++ set->entries--;+ recycleBin(set, bin);+ }+ + bin = next;+ }+ }+}
+ Chipmunk2D-7.0.2/src/cpHastySpace.c view
@@ -0,0 +1,694 @@+// Copyright 2013 Howling Moon Software. All rights reserved.+// See http://chipmunk2d.net/legal.php for more information.++#include <stdlib.h>+#include <stdio.h>++//TODO: Move all the thread stuff to another file++//#include <sys/param.h >+#ifndef _WIN32+#include <sys/sysctl.h>+#include <pthread.h>+#else+#ifndef WIN32_LEAN_AND_MEAN+#define WIN32_LEAN_AND_MEAN+#endif++#ifndef NOMINMAX+#define NOMINMAX+#endif++#include <process.h> // _beginthreadex+#include <windows.h>++#ifndef ETIMEDOUT+#define ETIMEDOUT 1+#endif++// Simple pthread implementation for Windows+// Made from scratch to avoid the LGPL licence from pthread-win32+enum {+ SIGNAL = 0,+ BROADCAST = 1,+ MAX_EVENTS = 2+};++typedef HANDLE pthread_t;+typedef struct+{+ // Based on http://www.cs.wustl.edu/~schmidt/win32-cv-1.html since Windows has no condition variable until NT6+ UINT waiters_count;+ // Count of the number of waiters.++ CRITICAL_SECTION waiters_count_lock;+ // Serialize access to <waiters_count_>.++ HANDLE events[MAX_EVENTS];+} pthread_cond_t;+typedef CRITICAL_SECTION pthread_mutex_t;++typedef struct {} pthread_condattr_t; // Dummy;++int pthread_cond_destroy(pthread_cond_t* cv)+{+ CloseHandle(cv->events[BROADCAST]);+ CloseHandle(cv->events[SIGNAL]);++ DeleteCriticalSection(&cv->waiters_count_lock);++ return 0;+}++int pthread_cond_init(pthread_cond_t* cv, const pthread_condattr_t* attr)+{+ // Initialize the count to 0.+ cv->waiters_count = 0;++ // Create an auto-reset event.+ cv->events[SIGNAL] = CreateEvent(NULL, // no security+ FALSE, // auto-reset event+ FALSE, // non-signaled initially+ NULL); // unnamed++ // Create a manual-reset event.+ cv->events[BROADCAST] = CreateEvent(NULL, // no security+ TRUE, // manual-reset+ FALSE, // non-signaled initially+ NULL); // unnamed++ InitializeCriticalSection(&cv->waiters_count_lock);++ return 0;+}++int pthread_cond_broadcast(pthread_cond_t *cv)+{+ // Avoid race conditions.+ EnterCriticalSection(&cv->waiters_count_lock);+ int have_waiters = cv->waiters_count > 0;+ LeaveCriticalSection(&cv->waiters_count_lock);++ if (have_waiters)+ SetEvent(cv->events[BROADCAST]);++ return 0;+}++int pthread_cond_signal(pthread_cond_t* cv)+{+ // Avoid race conditions.+ EnterCriticalSection(&cv->waiters_count_lock);+ int have_waiters = cv->waiters_count > 0;+ LeaveCriticalSection(&cv->waiters_count_lock);++ if (have_waiters)+ SetEvent(cv->events[SIGNAL]);++ return 0;+}++int pthread_cond_wait(pthread_cond_t* cv, pthread_mutex_t* external_mutex)+{+ // Avoid race conditions.+ EnterCriticalSection(&cv->waiters_count_lock);+ cv->waiters_count++;+ LeaveCriticalSection(&cv->waiters_count_lock);++ // It's ok to release the <external_mutex> here since Win32+ // manual-reset events maintain state when used with+ // <SetEvent>. This avoids the "lost wakeup" bug...+ LeaveCriticalSection(external_mutex);++ // Wait for either event to become signaled due to <pthread_cond_signal>+ // being called or <pthread_cond_broadcast> being called.+ int result = WaitForMultipleObjects(2, cv->events, FALSE, INFINITE);++ EnterCriticalSection(&cv->waiters_count_lock);+ cv->waiters_count--;+ int last_waiter =+ result == WAIT_OBJECT_0 + BROADCAST+ && cv->waiters_count == 0;+ LeaveCriticalSection(&cv->waiters_count_lock);++ // Some thread called <pthread_cond_broadcast>.+ if (last_waiter)+ // We're the last waiter to be notified or to stop waiting, so+ // reset the manual event. + ResetEvent(cv->events[BROADCAST]);++ // Reacquire the <external_mutex>.+ EnterCriticalSection(external_mutex);++ return result == WAIT_TIMEOUT ? ETIMEDOUT : 0;+}++typedef struct {} pthread_mutexattr_t; //< Dummy++int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr)+{+ InitializeCriticalSection(mutex);+ return 0;+}++int pthread_mutex_destroy(pthread_mutex_t* mutex)+{+ DeleteCriticalSection(mutex);+ return 0;+}++int pthread_mutex_lock(pthread_mutex_t* mutex)+{+ EnterCriticalSection(mutex);+ return 0;+}++int pthread_mutex_unlock(pthread_mutex_t* mutex)+{+ LeaveCriticalSection(mutex);+ return 0;+}++typedef struct {} pthread_attr_t;++typedef struct+{+ void *(*start_routine) (void *);+ void* arg;+} pthread_internal_thread;++unsigned int __stdcall ThreadProc(void* userdata)+{+ pthread_internal_thread* ud = (pthread_internal_thread*) userdata;+ ud->start_routine(ud->arg);++ free(ud);++ return 0;+}++int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void *(*start_routine) (void *), void *arg)+{+ pthread_internal_thread* ud = (pthread_internal_thread*) malloc(sizeof(pthread_internal_thread));+ ud->start_routine = start_routine;+ ud->arg = arg;++ *thread = (HANDLE) (_beginthreadex(NULL, 0, &ThreadProc, ud, 0, NULL));+ if (!*thread)+ return 1;++ return 0;+}++int pthread_join(pthread_t thread, void **value_ptr)+{+ WaitForSingleObject(thread, INFINITE);+ CloseHandle(thread);++ return 0;+}++#endif++#include "chipmunk/chipmunk_private.h"+#include "chipmunk/cpHastySpace.h"+++//MARK: ARM NEON Solver++#if __ARM_NEON__+#include <arm_neon.h>++// Tested and known to work fine with Clang 3.0 and GCC 4.2+// Doesn't work with Clang 1.6, and I have no idea why.+#if defined(__clang_major__) && __clang_major__ < 3+ #error Compiler not supported.+#endif++#if CP_USE_DOUBLES+ #if !__arm64+ #error Cannot use CP_USE_DOUBLES on 32 bit ARM.+ #endif+ + typedef float64_t cpFloat_t;+ typedef float64x2_t cpFloatx2_t;+ #define vld vld1q_f64+ #define vdup_n vdupq_n_f64+ #define vst vst1q_f64+ #define vst_lane vst1q_lane_f64+ #define vadd vaddq_f64+ #define vsub vsubq_f64+ #define vpadd vpaddq_f64+ #define vmul vmulq_f64+ #define vmul_n vmulq_n_f64+ #define vneg vnegq_f64+ #define vget_lane vgetq_lane_f64+ #define vset_lane vsetq_lane_f64+ #define vmin vminq_f64+ #define vmax vmaxq_f64+ #define vrev(__a) __builtin_shufflevector(__a, __a, 1, 0)+#else+ typedef float32_t cpFloat_t;+ typedef float32x2_t cpFloatx2_t;+ #define vld vld1_f32+ #define vdup_n vdup_n_f32+ #define vst vst1_f32+ #define vst_lane vst1_lane_f32+ #define vadd vadd_f32+ #define vsub vsub_f32+ #define vpadd vpadd_f32+ #define vmul vmul_f32+ #define vmul_n vmul_n_f32+ #define vneg vneg_f32+ #define vget_lane vget_lane_f32+ #define vset_lane vset_lane_f32+ #define vmin vmin_f32+ #define vmax vmax_f32+ #define vrev vrev64_f32+#endif++// TODO could probably do better here, maybe using vcreate?+// especially for the constants+// Maybe use the {} notation for GCC/Clang?+static inline cpFloatx2_t+vmake(cpFloat_t x, cpFloat_t y)+{+// cpFloatx2_t v = {};+// v = vset_lane(x, v, 0);+// v = vset_lane(y, v, 1);+// +// return v;+ + // This might not be super compatible, but all the NEON headers use it...+ return (cpFloatx2_t){x, y};+}++static void+cpArbiterApplyImpulse_NEON(cpArbiter *arb)+{+ cpBody *a = arb->body_a;+ cpBody *b = arb->body_b;+ cpFloatx2_t surface_vr = vld((cpFloat_t *)&arb->surface_vr);+ cpFloatx2_t n = vld((cpFloat_t *)&arb->n);+ cpFloat_t friction = arb->u;+ + int numContacts = arb->count;+ struct cpContact *contacts = arb->contacts;+ for(int i=0; i<numContacts; i++){+ struct cpContact *con = contacts + i;+ cpFloatx2_t r1 = vld((cpFloat_t *)&con->r1);+ cpFloatx2_t r2 = vld((cpFloat_t *)&con->r2);+ + cpFloatx2_t perp = vmake(-1.0, 1.0);+ cpFloatx2_t r1p = vmul(vrev(r1), perp);+ cpFloatx2_t r2p = vmul(vrev(r2), perp);+ + cpFloatx2_t vBias_a = vld((cpFloat_t *)&a->v_bias);+ cpFloatx2_t vBias_b = vld((cpFloat_t *)&b->v_bias);+ cpFloatx2_t wBias = vmake(a->w_bias, b->w_bias);+ + cpFloatx2_t vb1 = vadd(vBias_a, vmul_n(r1p, vget_lane(wBias, 0)));+ cpFloatx2_t vb2 = vadd(vBias_b, vmul_n(r2p, vget_lane(wBias, 1)));+ cpFloatx2_t vbr = vsub(vb2, vb1);+ + cpFloatx2_t v_a = vld((cpFloat_t *)&a->v);+ cpFloatx2_t v_b = vld((cpFloat_t *)&b->v);+ cpFloatx2_t w = vmake(a->w, b->w);+ cpFloatx2_t v1 = vadd(v_a, vmul_n(r1p, vget_lane(w, 0)));+ cpFloatx2_t v2 = vadd(v_b, vmul_n(r2p, vget_lane(w, 1)));+ cpFloatx2_t vr = vsub(v2, v1);+ + cpFloatx2_t vbn_vrn = vpadd(vmul(vbr, n), vmul(vr, n));+ + cpFloatx2_t v_offset = vmake(con->bias, -con->bounce);+ cpFloatx2_t jOld = vmake(con->jBias, con->jnAcc);+ cpFloatx2_t jbn_jn = vmul_n(vsub(v_offset, vbn_vrn), con->nMass);+ jbn_jn = vmax(vadd(jOld, jbn_jn), vdup_n(0.0));+ cpFloatx2_t jApply = vsub(jbn_jn, jOld);+ + cpFloatx2_t t = vmul(vrev(n), perp);+ cpFloatx2_t vrt_tmp = vmul(vadd(vr, surface_vr), t);+ cpFloatx2_t vrt = vpadd(vrt_tmp, vrt_tmp);+ + cpFloatx2_t jtOld = {}; jtOld = vset_lane(con->jtAcc, jtOld, 0);+ cpFloatx2_t jtMax = vrev(vmul_n(jbn_jn, friction));+ cpFloatx2_t jt = vmul_n(vrt, -con->tMass);+ jt = vmax(vneg(jtMax), vmin(vadd(jtOld, jt), jtMax));+ cpFloatx2_t jtApply = vsub(jt, jtOld);+ + cpFloatx2_t i_inv = vmake(-a->i_inv, b->i_inv);+ cpFloatx2_t nperp = vmake(1.0, -1.0);+ + cpFloatx2_t jBias = vmul_n(n, vget_lane(jApply, 0));+ cpFloatx2_t jBiasCross = vmul(vrev(jBias), nperp);+ cpFloatx2_t biasCrosses = vpadd(vmul(r1, jBiasCross), vmul(r2, jBiasCross));+ wBias = vadd(wBias, vmul(i_inv, biasCrosses));+ + vBias_a = vsub(vBias_a, vmul_n(jBias, a->m_inv));+ vBias_b = vadd(vBias_b, vmul_n(jBias, b->m_inv));+ + cpFloatx2_t j = vadd(vmul_n(n, vget_lane(jApply, 1)), vmul_n(t, vget_lane(jtApply, 0)));+ cpFloatx2_t jCross = vmul(vrev(j), nperp);+ cpFloatx2_t crosses = vpadd(vmul(r1, jCross), vmul(r2, jCross));+ w = vadd(w, vmul(i_inv, crosses));+ + v_a = vsub(v_a, vmul_n(j, a->m_inv));+ v_b = vadd(v_b, vmul_n(j, b->m_inv));+ + // TODO would moving these earlier help pipeline them better?+ vst((cpFloat_t *)&a->v_bias, vBias_a);+ vst((cpFloat_t *)&b->v_bias, vBias_b);+ vst_lane((cpFloat_t *)&a->w_bias, wBias, 0);+ vst_lane((cpFloat_t *)&b->w_bias, wBias, 1);+ + vst((cpFloat_t *)&a->v, v_a);+ vst((cpFloat_t *)&b->v, v_b);+ vst_lane((cpFloat_t *)&a->w, w, 0);+ vst_lane((cpFloat_t *)&b->w, w, 1);+ + vst_lane((cpFloat_t *)&con->jBias, jbn_jn, 0);+ vst_lane((cpFloat_t *)&con->jnAcc, jbn_jn, 1);+ vst_lane((cpFloat_t *)&con->jtAcc, jt, 0);+ }+}++#endif++//MARK: PThreads++// Right now using more than 2 threads probably wont help your performance any.+// If you are using a ridiculous number of iterations it could help though.+#define MAX_THREADS 2++struct ThreadContext {+ pthread_t thread;+ cpHastySpace *space;+ unsigned long thread_num;+};++typedef void (*cpHastySpaceWorkFunction)(cpSpace *space, unsigned long worker, unsigned long worker_count);++struct cpHastySpace {+ cpSpace space;+ + // Number of worker threads (including the main thread)+ unsigned long num_threads;+ + // Number of worker threads currently executing. (also including the main thread)+ unsigned long num_working;+ + // Number of constraints (plus contacts) that must exist per step to start the worker threads.+ unsigned long constraint_count_threshold;+ + pthread_mutex_t mutex;+ pthread_cond_t cond_work, cond_resume;+ + // Work function to invoke.+ cpHastySpaceWorkFunction work;+ + struct ThreadContext workers[MAX_THREADS - 1];+};++static void *+WorkerThreadLoop(struct ThreadContext *context)+{+ cpHastySpace *hasty = context->space;+ + unsigned long thread = context->thread_num;+ unsigned long num_threads = hasty->num_threads;+ + for(;;){+ pthread_mutex_lock(&hasty->mutex); {+ if(--hasty->num_working == 0){+ pthread_cond_signal(&hasty->cond_resume);+ }+ + pthread_cond_wait(&hasty->cond_work, &hasty->mutex);+ } pthread_mutex_unlock(&hasty->mutex);+ + cpHastySpaceWorkFunction func = hasty->work;+ if(func){+ hasty->work(&hasty->space, thread, num_threads);+ } else {+ break;+ }+ }+ + return NULL;+}++static void+RunWorkers(cpHastySpace *hasty, cpHastySpaceWorkFunction func)+{+ hasty->num_working = hasty->num_threads - 1;+ hasty->work = func;+ + if(hasty->num_working > 0){+ pthread_mutex_lock(&hasty->mutex); {+ pthread_cond_broadcast(&hasty->cond_work);+ } pthread_mutex_unlock(&hasty->mutex);+ + func((cpSpace *)hasty, 0, hasty->num_threads);+ + pthread_mutex_lock(&hasty->mutex); {+ if(hasty->num_working > 0){+ pthread_cond_wait(&hasty->cond_resume, &hasty->mutex);+ }+ } pthread_mutex_unlock(&hasty->mutex);+ } else {+ func((cpSpace *)hasty, 0, hasty->num_threads);+ }+ + hasty->work = NULL;+}++static void+Solver(cpSpace *space, unsigned long worker, unsigned long worker_count)+{+ cpArray *constraints = space->constraints;+ cpArray *arbiters = space->arbiters;+ + cpFloat dt = space->curr_dt;+ unsigned long iterations = (space->iterations + worker_count - 1)/worker_count;+ + for(unsigned long i=0; i<iterations; i++){+ for(int j=0; j<arbiters->num; j++){+ cpArbiter *arb = (cpArbiter *)arbiters->arr[j];+ #ifdef __ARM_NEON__+ cpArbiterApplyImpulse_NEON(arb);+ #else+ cpArbiterApplyImpulse(arb);+ #endif+ }+ + for(int j=0; j<constraints->num; j++){+ cpConstraint *constraint = (cpConstraint *)constraints->arr[j];+ constraint->klass->applyImpulse(constraint, dt);+ }+ }+}++//MARK: Thread Management Functions++static void+HaltThreads(cpHastySpace *hasty)+{+ pthread_mutex_t *mutex = &hasty->mutex;+ pthread_mutex_lock(mutex); {+ hasty->work = NULL; // NULL work function means break and exit+ pthread_cond_broadcast(&hasty->cond_work);+ } pthread_mutex_unlock(mutex);+ + for(unsigned long i=0; i<(hasty->num_threads-1); i++){+ pthread_join(hasty->workers[i].thread, NULL);+ }+}++void+cpHastySpaceSetThreads(cpSpace *space, unsigned long threads)+{+#if TARGET_IPHONE_SIMULATOR == 1+ // Individual values appear to be written non-atomically when compiled as debug for the simulator.+ // No idea why, so threads are disabled.+ threads = 1;+#endif + + cpHastySpace *hasty = (cpHastySpace *)space;+ HaltThreads(hasty);+ +#ifdef __APPLE__+ if(threads == 0){+ size_t size = sizeof(threads);+ sysctlbyname("hw.ncpu", &threads, &size, NULL, 0);+ }+#else+ if(threads == 0) threads = 1;+#endif+ + hasty->num_threads = (threads < MAX_THREADS ? threads : MAX_THREADS);+ hasty->num_working = hasty->num_threads - 1;+ + // Create the worker threads and wait for them to signal ready.+ if(hasty->num_working > 0){+ pthread_mutex_lock(&hasty->mutex);+ for(unsigned long i=0; i<(hasty->num_threads-1); i++){+ hasty->workers[i].space = hasty;+ hasty->workers[i].thread_num = i + 1;+ + pthread_create(&hasty->workers[i].thread, NULL, (void*(*)(void*))WorkerThreadLoop, &hasty->workers[i]);+ }+ + pthread_cond_wait(&hasty->cond_resume, &hasty->mutex);+ pthread_mutex_unlock(&hasty->mutex);+ }+}++unsigned long+cpHastySpaceGetThreads(cpSpace *space)+{+ return ((cpHastySpace *)space)->num_threads;+}++//MARK: Overriden cpSpace Functions.++cpSpace *+cpHastySpaceNew(void)+{+ cpHastySpace *hasty = (cpHastySpace *)cpcalloc(1, sizeof(cpHastySpace));+ cpSpaceInit((cpSpace *)hasty);+ + pthread_mutex_init(&hasty->mutex, NULL);+ pthread_cond_init(&hasty->cond_work, NULL);+ pthread_cond_init(&hasty->cond_resume, NULL);+ + // TODO magic number, should test this more thoroughly.+ hasty->constraint_count_threshold = 50;+ + // Default to 1 thread for determinism.+ hasty->num_threads = 1;+ cpHastySpaceSetThreads((cpSpace *)hasty, 1);++ return (cpSpace *)hasty;+}++void+cpHastySpaceFree(cpSpace *space)+{+ cpHastySpace *hasty = (cpHastySpace *)space;+ + HaltThreads(hasty);+ + pthread_mutex_destroy(&hasty->mutex);+ pthread_cond_destroy(&hasty->cond_work);+ pthread_cond_destroy(&hasty->cond_resume);+ + cpSpaceFree(space);+}++void+cpHastySpaceStep(cpSpace *space, cpFloat dt)+{+ // don't step if the timestep is 0!+ if(dt == 0.0f) return;+ + space->stamp++;+ + cpFloat prev_dt = space->curr_dt;+ space->curr_dt = dt;+ + cpArray *bodies = space->dynamicBodies;+ cpArray *constraints = space->constraints;+ cpArray *arbiters = space->arbiters;+ + // Reset and empty the arbiter list.+ for(int i=0; i<arbiters->num; i++){+ cpArbiter *arb = (cpArbiter *)arbiters->arr[i];+ arb->state = CP_ARBITER_STATE_NORMAL;+ + // If both bodies are awake, unthread the arbiter from the contact graph.+ if(!cpBodyIsSleeping(arb->body_a) && !cpBodyIsSleeping(arb->body_b)){+ cpArbiterUnthread(arb);+ }+ }+ arbiters->num = 0;+ + cpSpaceLock(space); {+ // Integrate positions+ for(int i=0; i<bodies->num; i++){+ cpBody *body = (cpBody *)bodies->arr[i];+ body->position_func(body, dt);+ }+ + // Find colliding pairs.+ cpSpacePushFreshContactBuffer(space);+ cpSpatialIndexEach(space->dynamicShapes, (cpSpatialIndexIteratorFunc)cpShapeUpdateFunc, NULL);+ cpSpatialIndexReindexQuery(space->dynamicShapes, (cpSpatialIndexQueryFunc)cpSpaceCollideShapes, space);+ } cpSpaceUnlock(space, cpFalse);+ + // Rebuild the contact graph (and detect sleeping components if sleeping is enabled)+ cpSpaceProcessComponents(space, dt);+ + cpSpaceLock(space); {+ // Clear out old cached arbiters and call separate callbacks+ cpHashSetFilter(space->cachedArbiters, (cpHashSetFilterFunc)cpSpaceArbiterSetFilter, space);++ // Prestep the arbiters and constraints.+ cpFloat slop = space->collisionSlop;+ cpFloat biasCoef = 1.0f - cpfpow(space->collisionBias, dt);+ for(int i=0; i<arbiters->num; i++){+ cpArbiterPreStep((cpArbiter *)arbiters->arr[i], dt, slop, biasCoef);+ }++ for(int i=0; i<constraints->num; i++){+ cpConstraint *constraint = (cpConstraint *)constraints->arr[i];+ + cpConstraintPreSolveFunc preSolve = constraint->preSolve;+ if(preSolve) preSolve(constraint, space);+ + constraint->klass->preStep(constraint, dt);+ }+ + // Integrate velocities.+ cpFloat damping = cpfpow(space->damping, dt);+ cpVect gravity = space->gravity;+ for(int i=0; i<bodies->num; i++){+ cpBody *body = (cpBody *)bodies->arr[i];+ body->velocity_func(body, gravity, damping, dt);+ }+ + // Apply cached impulses+ cpFloat dt_coef = (prev_dt == 0.0f ? 0.0f : dt/prev_dt);+ for(int i=0; i<arbiters->num; i++){+ cpArbiterApplyCachedImpulse((cpArbiter *)arbiters->arr[i], dt_coef);+ }+ + for(int i=0; i<constraints->num; i++){+ cpConstraint *constraint = (cpConstraint *)constraints->arr[i];+ constraint->klass->applyCachedImpulse(constraint, dt_coef);+ }+ + // Run the impulse solver.+ cpHastySpace *hasty = (cpHastySpace *)space;+ if((unsigned long)(arbiters->num + constraints->num) > hasty->constraint_count_threshold){+ RunWorkers(hasty, Solver);+ } else {+ Solver(space, 0, 1);+ }+ + // Run the constraint post-solve callbacks+ for(int i=0; i<constraints->num; i++){+ cpConstraint *constraint = (cpConstraint *)constraints->arr[i];+ + cpConstraintPostSolveFunc postSolve = constraint->postSolve;+ if(postSolve) postSolve(constraint, space);+ }+ + // run the post-solve callbacks+ for(int i=0; i<arbiters->num; i++){+ cpArbiter *arb = (cpArbiter *) arbiters->arr[i];+ + cpCollisionHandler *handler = arb->handler;+ handler->postSolveFunc(arb, space, handler->userData);+ }+ } cpSpaceUnlock(space, cpTrue);+}
+ Chipmunk2D-7.0.2/src/cpMarch.c view
@@ -0,0 +1,157 @@+// Copyright 2013 Howling Moon Software. All rights reserved.+// See http://chipmunk2d.net/legal.php for more information.++#include <stdlib.h>+#include <stdio.h>+#include <math.h>++#include "chipmunk/chipmunk.h"+#include "chipmunk/cpMarch.h"+++typedef void (*cpMarchCellFunc)(+ cpFloat t, cpFloat a, cpFloat b, cpFloat c, cpFloat d,+ cpFloat x0, cpFloat x1, cpFloat y0, cpFloat y1,+ cpMarchSegmentFunc segment, void *segment_data+);++// The looping and sample caching code is shared between cpMarchHard() and cpMarchSoft().+static void+cpMarchCells(+ cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat t,+ cpMarchSegmentFunc segment, void *segment_data,+ cpMarchSampleFunc sample, void *sample_data,+ cpMarchCellFunc cell+){+ cpFloat x_denom = 1.0/(cpFloat)(x_samples - 1);+ cpFloat y_denom = 1.0/(cpFloat)(y_samples - 1);+ + // TODO range assertions and short circuit for 0 sized windows.+ + // Keep a copy of the previous row to avoid double lookups.+ cpFloat *buffer = (cpFloat *)cpcalloc(x_samples, sizeof(cpFloat));+ for(unsigned long i=0; i<x_samples; i++) buffer[i] = sample(cpv(cpflerp(bb.l, bb.r, i*x_denom), bb.b), sample_data);+ + for(unsigned long j=0; j<y_samples-1; j++){+ cpFloat y0 = cpflerp(bb.b, bb.t, (j+0)*y_denom);+ cpFloat y1 = cpflerp(bb.b, bb.t, (j+1)*y_denom);+ + cpFloat a, b = buffer[0];+ cpFloat c, d = sample(cpv(bb.l, y1), sample_data);+ buffer[0] = d;+ + for(unsigned long i=0; i<x_samples-1; i++){+ cpFloat x0 = cpflerp(bb.l, bb.r, (i+0)*x_denom);+ cpFloat x1 = cpflerp(bb.l, bb.r, (i+1)*x_denom);+ + a = b, b = buffer[i + 1];+ c = d, d = sample(cpv(x1, y1), sample_data);+ buffer[i + 1] = d;+ + cell(t, a, b, c, d, x0, x1, y0, y1, segment, segment_data);+ }+ }+ + cpfree(buffer);+}+++// TODO should flip this around eventually.+static inline void+seg(cpVect v0, cpVect v1, cpMarchSegmentFunc f, void *data)+{+ if(!cpveql(v0, v1)) f(v1, v0, data);+}++// Lerps between two positions based on their sample values.+static inline cpFloat+midlerp(cpFloat x0, cpFloat x1, cpFloat s0, cpFloat s1, cpFloat t)+{+ return cpflerp(x0, x1, (t - s0)/(s1 - s0));+}++static void+cpMarchCellSoft(+ cpFloat t, cpFloat a, cpFloat b, cpFloat c, cpFloat d,+ cpFloat x0, cpFloat x1, cpFloat y0, cpFloat y1,+ cpMarchSegmentFunc segment, void *segment_data+){+ // TODO this switch part is super expensive, can it be NEONized?+ switch((a>t)<<0 | (b>t)<<1 | (c>t)<<2 | (d>t)<<3){+ case 0x1: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data); break;+ case 0x2: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data); break;+ case 0x3: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data); break;+ case 0x4: seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break;+ case 0x5: seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data); break;+ case 0x6: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data);+ seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break;+ case 0x7: seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data); break;+ case 0x8: seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break;+ case 0x9: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data);+ seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break;+ case 0xA: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break;+ case 0xB: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break;+ case 0xC: seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break;+ case 0xD: seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data); break;+ case 0xE: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break;+ default: break; // 0x0 and 0xF+ }+}++void+cpMarchSoft(+ cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat t,+ cpMarchSegmentFunc segment, void *segment_data,+ cpMarchSampleFunc sample, void *sample_data+){+ cpMarchCells(bb, x_samples, y_samples, t, segment, segment_data, sample, sample_data, cpMarchCellSoft);+}+++// TODO should flip this around eventually.+static inline void+segs(cpVect a, cpVect b, cpVect c, cpMarchSegmentFunc f, void *data)+{+ seg(b, c, f, data);+ seg(a, b, f, data);+}++static void+cpMarchCellHard(+ cpFloat t, cpFloat a, cpFloat b, cpFloat c, cpFloat d,+ cpFloat x0, cpFloat x1, cpFloat y0, cpFloat y1,+ cpMarchSegmentFunc segment, void *segment_data+){+ // midpoints+ cpFloat xm = cpflerp(x0, x1, 0.5f);+ cpFloat ym = cpflerp(y0, y1, 0.5f);+ + switch((a>t)<<0 | (b>t)<<1 | (c>t)<<2 | (d>t)<<3){+ case 0x1: segs(cpv(x0, ym), cpv(xm, ym), cpv(xm, y0), segment, segment_data); break;+ case 0x2: segs(cpv(xm, y0), cpv(xm, ym), cpv(x1, ym), segment, segment_data); break;+ case 0x3: seg(cpv(x0, ym), cpv(x1, ym), segment, segment_data); break;+ case 0x4: segs(cpv(xm, y1), cpv(xm, ym), cpv(x0, ym), segment, segment_data); break;+ case 0x5: seg(cpv(xm, y1), cpv(xm, y0), segment, segment_data); break;+ case 0x6: segs(cpv(xm, y0), cpv(xm, ym), cpv(x0, ym), segment, segment_data);+ segs(cpv(xm, y1), cpv(xm, ym), cpv(x1, ym), segment, segment_data); break;+ case 0x7: segs(cpv(xm, y1), cpv(xm, ym), cpv(x1, ym), segment, segment_data); break;+ case 0x8: segs(cpv(x1, ym), cpv(xm, ym), cpv(xm, y1), segment, segment_data); break;+ case 0x9: segs(cpv(x1, ym), cpv(xm, ym), cpv(xm, y0), segment, segment_data);+ segs(cpv(x0, ym), cpv(xm, ym), cpv(xm, y1), segment, segment_data); break;+ case 0xA: seg(cpv(xm, y0), cpv(xm, y1), segment, segment_data); break;+ case 0xB: segs(cpv(x0, ym), cpv(xm, ym), cpv(xm, y1), segment, segment_data); break;+ case 0xC: seg(cpv(x1, ym), cpv(x0, ym), segment, segment_data); break;+ case 0xD: segs(cpv(x1, ym), cpv(xm, ym), cpv(xm, y0), segment, segment_data); break;+ case 0xE: segs(cpv(xm, y0), cpv(xm, ym), cpv(x0, ym), segment, segment_data); break;+ default: break; // 0x0 and 0xF+ }+}++void+cpMarchHard(+ cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat t,+ cpMarchSegmentFunc segment, void *segment_data,+ cpMarchSampleFunc sample, void *sample_data+){+ cpMarchCells(bb, x_samples, y_samples, t, segment, segment_data, sample, sample_data, cpMarchCellHard);+}
+ Chipmunk2D-7.0.2/src/cpPinJoint.c view
@@ -0,0 +1,172 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++static void+preStep(cpPinJoint *joint, cpFloat dt)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + joint->r1 = cpTransformVect(a->transform, cpvsub(joint->anchorA, a->cog));+ joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog));+ + cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1));+ cpFloat dist = cpvlength(delta);+ joint->n = cpvmult(delta, 1.0f/(dist ? dist : (cpFloat)INFINITY));+ + // calculate mass normal+ joint->nMass = 1.0f/k_scalar(a, b, joint->r1, joint->r2, joint->n);+ + // calculate bias velocity+ cpFloat maxBias = joint->constraint.maxBias;+ joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*(dist - joint->dist)/dt, -maxBias, maxBias);+}++static void+applyCachedImpulse(cpPinJoint *joint, cpFloat dt_coef)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + cpVect j = cpvmult(joint->n, joint->jnAcc*dt_coef);+ apply_impulses(a, b, joint->r1, joint->r2, j);+}++static void+applyImpulse(cpPinJoint *joint, cpFloat dt)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ cpVect n = joint->n;++ // compute relative velocity+ cpFloat vrn = normal_relative_velocity(a, b, joint->r1, joint->r2, n);+ + cpFloat jnMax = joint->constraint.maxForce*dt;+ + // compute normal impulse+ cpFloat jn = (joint->bias - vrn)*joint->nMass;+ cpFloat jnOld = joint->jnAcc;+ joint->jnAcc = cpfclamp(jnOld + jn, -jnMax, jnMax);+ jn = joint->jnAcc - jnOld;+ + // apply impulse+ apply_impulses(a, b, joint->r1, joint->r2, cpvmult(n, jn));+}++static cpFloat+getImpulse(cpPinJoint *joint)+{+ return cpfabs(joint->jnAcc);+}++static const cpConstraintClass klass = {+ (cpConstraintPreStepImpl)preStep,+ (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,+ (cpConstraintApplyImpulseImpl)applyImpulse,+ (cpConstraintGetImpulseImpl)getImpulse,+};+++cpPinJoint *+cpPinJointAlloc(void)+{+ return (cpPinJoint *)cpcalloc(1, sizeof(cpPinJoint));+}++cpPinJoint *+cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB)+{+ cpConstraintInit((cpConstraint *)joint, &klass, a, b);+ + joint->anchorA = anchorA;+ joint->anchorB = anchorB;+ + // STATIC_BODY_CHECK+ cpVect p1 = (a ? cpTransformPoint(a->transform, anchorA) : anchorA);+ cpVect p2 = (b ? cpTransformPoint(b->transform, anchorB) : anchorB);+ joint->dist = cpvlength(cpvsub(p2, p1));+ + cpAssertWarn(joint->dist > 0.0, "You created a 0 length pin joint. A pivot joint will be much more stable.");++ joint->jnAcc = 0.0f;+ + return joint;+}++cpConstraint *+cpPinJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB)+{+ return (cpConstraint *)cpPinJointInit(cpPinJointAlloc(), a, b, anchorA, anchorB);+}++cpBool+cpConstraintIsPinJoint(const cpConstraint *constraint)+{+ return (constraint->klass == &klass);+}++cpVect+cpPinJointGetAnchorA(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint.");+ return ((cpPinJoint *)constraint)->anchorA;+}++void+cpPinJointSetAnchorA(cpConstraint *constraint, cpVect anchorA)+{+ cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint.");+ cpConstraintActivateBodies(constraint);+ ((cpPinJoint *)constraint)->anchorA = anchorA;+}++cpVect+cpPinJointGetAnchorB(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint.");+ return ((cpPinJoint *)constraint)->anchorB;+}++void+cpPinJointSetAnchorB(cpConstraint *constraint, cpVect anchorB)+{+ cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint.");+ cpConstraintActivateBodies(constraint);+ ((cpPinJoint *)constraint)->anchorB = anchorB;+}++cpFloat+cpPinJointGetDist(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint.");+ return ((cpPinJoint *)constraint)->dist;+}++void+cpPinJointSetDist(cpConstraint *constraint, cpFloat dist)+{+ cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint.");+ cpConstraintActivateBodies(constraint);+ ((cpPinJoint *)constraint)->dist = dist;+}
+ Chipmunk2D-7.0.2/src/cpPivotJoint.c view
@@ -0,0 +1,152 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++static void+preStep(cpPivotJoint *joint, cpFloat dt)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + joint->r1 = cpTransformVect(a->transform, cpvsub(joint->anchorA, a->cog));+ joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog));+ + // Calculate mass tensor+ joint-> k = k_tensor(a, b, joint->r1, joint->r2);+ + // calculate bias velocity+ cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1));+ joint->bias = cpvclamp(cpvmult(delta, -bias_coef(joint->constraint.errorBias, dt)/dt), joint->constraint.maxBias);+}++static void+applyCachedImpulse(cpPivotJoint *joint, cpFloat dt_coef)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + apply_impulses(a, b, joint->r1, joint->r2, cpvmult(joint->jAcc, dt_coef));+}++static void+applyImpulse(cpPivotJoint *joint, cpFloat dt)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + cpVect r1 = joint->r1;+ cpVect r2 = joint->r2;+ + // compute relative velocity+ cpVect vr = relative_velocity(a, b, r1, r2);+ + // compute normal impulse+ cpVect j = cpMat2x2Transform(joint->k, cpvsub(joint->bias, vr));+ cpVect jOld = joint->jAcc;+ joint->jAcc = cpvclamp(cpvadd(joint->jAcc, j), joint->constraint.maxForce*dt);+ j = cpvsub(joint->jAcc, jOld);+ + // apply impulse+ apply_impulses(a, b, joint->r1, joint->r2, j);+}++static cpFloat+getImpulse(cpConstraint *joint)+{+ return cpvlength(((cpPivotJoint *)joint)->jAcc);+}++static const cpConstraintClass klass = {+ (cpConstraintPreStepImpl)preStep,+ (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,+ (cpConstraintApplyImpulseImpl)applyImpulse,+ (cpConstraintGetImpulseImpl)getImpulse,+};++cpPivotJoint *+cpPivotJointAlloc(void)+{+ return (cpPivotJoint *)cpcalloc(1, sizeof(cpPivotJoint));+}++cpPivotJoint *+cpPivotJointInit(cpPivotJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB)+{+ cpConstraintInit((cpConstraint *)joint, &klass, a, b);+ + joint->anchorA = anchorA;+ joint->anchorB = anchorB;+ + joint->jAcc = cpvzero;+ + return joint;+}++cpConstraint *+cpPivotJointNew2(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB)+{+ return (cpConstraint *)cpPivotJointInit(cpPivotJointAlloc(), a, b, anchorA, anchorB);+}++cpConstraint *+cpPivotJointNew(cpBody *a, cpBody *b, cpVect pivot)+{+ cpVect anchorA = (a ? cpBodyWorldToLocal(a, pivot) : pivot);+ cpVect anchorB = (b ? cpBodyWorldToLocal(b, pivot) : pivot);+ return cpPivotJointNew2(a, b, anchorA, anchorB);+}++cpBool+cpConstraintIsPivotJoint(const cpConstraint *constraint)+{+ return (constraint->klass == &klass);+}++cpVect+cpPivotJointGetAnchorA(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint.");+ return ((cpPivotJoint *)constraint)->anchorA;+}++void+cpPivotJointSetAnchorA(cpConstraint *constraint, cpVect anchorA)+{+ cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint.");+ cpConstraintActivateBodies(constraint);+ ((cpPivotJoint *)constraint)->anchorA = anchorA;+}++cpVect+cpPivotJointGetAnchorB(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint.");+ return ((cpPivotJoint *)constraint)->anchorB;+}++void+cpPivotJointSetAnchorB(cpConstraint *constraint, cpVect anchorB)+{+ cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint.");+ cpConstraintActivateBodies(constraint);+ ((cpPivotJoint *)constraint)->anchorB = anchorB;+}
+ Chipmunk2D-7.0.2/src/cpPolyShape.c view
@@ -0,0 +1,323 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"+#include "chipmunk/chipmunk_unsafe.h"++cpPolyShape *+cpPolyShapeAlloc(void)+{+ return (cpPolyShape *)cpcalloc(1, sizeof(cpPolyShape));+}++static void+cpPolyShapeDestroy(cpPolyShape *poly)+{+ if(poly->count > CP_POLY_SHAPE_INLINE_ALLOC){+ cpfree(poly->planes);+ }+}++static cpBB+cpPolyShapeCacheData(cpPolyShape *poly, cpTransform transform)+{+ int count = poly->count;+ struct cpSplittingPlane *dst = poly->planes;+ struct cpSplittingPlane *src = dst + count;+ + cpFloat l = (cpFloat)INFINITY, r = -(cpFloat)INFINITY;+ cpFloat b = (cpFloat)INFINITY, t = -(cpFloat)INFINITY;+ + for(int i=0; i<count; i++){+ cpVect v = cpTransformPoint(transform, src[i].v0);+ cpVect n = cpTransformVect(transform, src[i].n);+ + dst[i].v0 = v;+ dst[i].n = n;+ + l = cpfmin(l, v.x);+ r = cpfmax(r, v.x);+ b = cpfmin(b, v.y);+ t = cpfmax(t, v.y);+ }+ + cpFloat radius = poly->r;+ return (poly->shape.bb = cpBBNew(l - radius, b - radius, r + radius, t + radius));+}++static void+cpPolyShapePointQuery(cpPolyShape *poly, cpVect p, cpPointQueryInfo *info){+ int count = poly->count;+ struct cpSplittingPlane *planes = poly->planes;+ cpFloat r = poly->r;+ + cpVect v0 = planes[count - 1].v0;+ cpFloat minDist = INFINITY;+ cpVect closestPoint = cpvzero;+ cpVect closestNormal = cpvzero;+ cpBool outside = cpFalse;+ + for(int i=0; i<count; i++){+ cpVect v1 = planes[i].v0;+ outside = outside || (cpvdot(planes[i].n, cpvsub(p,v1)) > 0.0f);+ + cpVect closest = cpClosetPointOnSegment(p, v0, v1);+ + cpFloat dist = cpvdist(p, closest);+ if(dist < minDist){+ minDist = dist;+ closestPoint = closest;+ closestNormal = planes[i].n;+ }+ + v0 = v1;+ }+ + cpFloat dist = (outside ? minDist : -minDist);+ cpVect g = cpvmult(cpvsub(p, closestPoint), 1.0f/dist);+ + info->shape = (cpShape *)poly;+ info->point = cpvadd(closestPoint, cpvmult(g, r));+ info->distance = dist - r;+ + // Use the normal of the closest segment if the distance is small.+ info->gradient = (minDist > MAGIC_EPSILON ? g : closestNormal);+}++static void+cpPolyShapeSegmentQuery(cpPolyShape *poly, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info)+{+ struct cpSplittingPlane *planes = poly->planes;+ int count = poly->count;+ cpFloat r = poly->r;+ cpFloat rsum = r + r2;+ + for(int i=0; i<count; i++){+ cpVect n = planes[i].n;+ cpFloat an = cpvdot(a, n);+ cpFloat d = an - cpvdot(planes[i].v0, n) - rsum;+ if(d < 0.0f) continue;+ + cpFloat bn = cpvdot(b, n);+ cpFloat t = d/(an - bn);+ if(t < 0.0f || 1.0f < t) continue;+ + cpVect point = cpvlerp(a, b, t);+ cpFloat dt = cpvcross(n, point);+ cpFloat dtMin = cpvcross(n, planes[(i - 1 + count)%count].v0);+ cpFloat dtMax = cpvcross(n, planes[i].v0);+ + if(dtMin <= dt && dt <= dtMax){+ info->shape = (cpShape *)poly;+ info->point = cpvsub(cpvlerp(a, b, t), cpvmult(n, r2));+ info->normal = n;+ info->alpha = t;+ }+ }+ + // Also check against the beveled vertexes.+ if(rsum > 0.0f){+ for(int i=0; i<count; i++){+ cpSegmentQueryInfo circle_info = {NULL, b, cpvzero, 1.0f};+ CircleSegmentQuery(&poly->shape, planes[i].v0, r, a, b, r2, &circle_info);+ if(circle_info.alpha < info->alpha) (*info) = circle_info;+ }+ }+}++static void+SetVerts(cpPolyShape *poly, int count, const cpVect *verts)+{+ poly->count = count;+ if(count <= CP_POLY_SHAPE_INLINE_ALLOC){+ poly->planes = poly->_planes;+ } else {+ poly->planes = (struct cpSplittingPlane *)cpcalloc(2*count, sizeof(struct cpSplittingPlane));+ }+ + for(int i=0; i<count; i++){+ cpVect a = verts[(i - 1 + count)%count];+ cpVect b = verts[i];+ cpVect n = cpvnormalize(cpvrperp(cpvsub(b, a)));+ + poly->planes[i + count].v0 = b;+ poly->planes[i + count].n = n;+ }+}++static struct cpShapeMassInfo+cpPolyShapeMassInfo(cpFloat mass, int count, const cpVect *verts, cpFloat radius)+{+ // TODO moment is approximate due to radius.+ + cpVect centroid = cpCentroidForPoly(count, verts);+ struct cpShapeMassInfo info = {+ mass, cpMomentForPoly(1.0f, count, verts, cpvneg(centroid), radius),+ centroid,+ cpAreaForPoly(count, verts, radius),+ };+ + return info;+}++static const cpShapeClass polyClass = {+ CP_POLY_SHAPE,+ (cpShapeCacheDataImpl)cpPolyShapeCacheData,+ (cpShapeDestroyImpl)cpPolyShapeDestroy,+ (cpShapePointQueryImpl)cpPolyShapePointQuery,+ (cpShapeSegmentQueryImpl)cpPolyShapeSegmentQuery,+};++cpPolyShape *+cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius)+{+ cpVect *hullVerts = (cpVect *)alloca(count*sizeof(cpVect));+ + // Transform the verts before building the hull in case of a negative scale.+ for(int i=0; i<count; i++) hullVerts[i] = cpTransformPoint(transform, verts[i]);+ + unsigned int hullCount = cpConvexHull(count, hullVerts, hullVerts, NULL, 0.0);+ return cpPolyShapeInitRaw(poly, body, hullCount, hullVerts, radius);+}++cpPolyShape *+cpPolyShapeInitRaw(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpFloat radius)+{+ cpShapeInit((cpShape *)poly, &polyClass, body, cpPolyShapeMassInfo(0.0f, count, verts, radius));+ + SetVerts(poly, count, verts);+ poly->r = radius;++ return poly;+}++cpShape *+cpPolyShapeNew(cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius)+{+ return (cpShape *)cpPolyShapeInit(cpPolyShapeAlloc(), body, count, verts, transform, radius);+}++cpShape *+cpPolyShapeNewRaw(cpBody *body, int count, const cpVect *verts, cpFloat radius)+{+ return (cpShape *)cpPolyShapeInitRaw(cpPolyShapeAlloc(), body, count, verts, radius);+}++cpPolyShape *+cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height, cpFloat radius)+{+ cpFloat hw = width/2.0f;+ cpFloat hh = height/2.0f;+ + return cpBoxShapeInit2(poly, body, cpBBNew(-hw, -hh, hw, hh), radius);+}++cpPolyShape *+cpBoxShapeInit2(cpPolyShape *poly, cpBody *body, cpBB box, cpFloat radius)+{+ cpVect verts[] = {+ cpv(box.r, box.b),+ cpv(box.r, box.t),+ cpv(box.l, box.t),+ cpv(box.l, box.b),+ };+ + return cpPolyShapeInitRaw(poly, body, 4, verts, radius);+}++cpShape *+cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height, cpFloat radius)+{+ return (cpShape *)cpBoxShapeInit(cpPolyShapeAlloc(), body, width, height, radius);+}++cpShape *+cpBoxShapeNew2(cpBody *body, cpBB box, cpFloat radius)+{+ return (cpShape *)cpBoxShapeInit2(cpPolyShapeAlloc(), body, box, radius);+}++int+cpPolyShapeGetCount(const cpShape *shape)+{+ cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");+ return ((cpPolyShape *)shape)->count;+}++cpVect+cpPolyShapeGetVert(const cpShape *shape, int i)+{+ cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");+ + int count = cpPolyShapeGetCount(shape);+ cpAssertHard(0 <= i && i < count, "Index out of range.");+ + return ((cpPolyShape *)shape)->planes[i + count].v0;+}++cpFloat+cpPolyShapeGetRadius(const cpShape *shape)+{+ cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");+ return ((cpPolyShape *)shape)->r;+}++// Unsafe API (chipmunk_unsafe.h)++void+cpPolyShapeSetVerts(cpShape *shape, int count, cpVect *verts, cpTransform transform)+{+ cpVect *hullVerts = (cpVect *)alloca(count*sizeof(cpVect));+ + // Transform the verts before building the hull in case of a negative scale.+ for(int i=0; i<count; i++) hullVerts[i] = cpTransformPoint(transform, verts[i]);+ + unsigned int hullCount = cpConvexHull(count, hullVerts, hullVerts, NULL, 0.0);+ cpPolyShapeSetVertsRaw(shape, hullCount, hullVerts);+}++void+cpPolyShapeSetVertsRaw(cpShape *shape, int count, cpVect *verts)+{+ cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");+ cpPolyShape *poly = (cpPolyShape *)shape;+ cpPolyShapeDestroy(poly);+ + SetVerts(poly, count, verts);+ + cpFloat mass = shape->massInfo.m;+ shape->massInfo = cpPolyShapeMassInfo(shape->massInfo.m, count, verts, poly->r);+ if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);+}++void+cpPolyShapeSetRadius(cpShape *shape, cpFloat radius)+{+ cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");+ cpPolyShape *poly = (cpPolyShape *)shape;+ poly->r = radius;+ + + // TODO radius is not handled by moment/area+// cpFloat mass = shape->massInfo.m;+// shape->massInfo = cpPolyShapeMassInfo(shape->massInfo.m, poly->count, poly->verts, poly->r);+// if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);+}
+ Chipmunk2D-7.0.2/src/cpPolyline.c view
@@ -0,0 +1,652 @@+// Copyright 2013 Howling Moon Software. All rights reserved.+// See http://chipmunk2d.net/legal.php for more information.++#include <stdlib.h>+#include <stdio.h>+#include <string.h>+#include <math.h>++#include "chipmunk/chipmunk_private.h"+#include "chipmunk/cpPolyline.h"+++static inline int Next(int i, int count){return (i+1)%count;}++//MARK: Polylines++#define DEFAULT_POLYLINE_CAPACITY 16++static int+cpPolylineSizeForCapacity(int capacity)+{+ return sizeof(cpPolyline) + capacity*sizeof(cpVect);+}++static cpPolyline *+cpPolylineMake(int capacity)+{+ capacity = (capacity > DEFAULT_POLYLINE_CAPACITY ? capacity : DEFAULT_POLYLINE_CAPACITY);+ + cpPolyline *line = (cpPolyline *)cpcalloc(1, cpPolylineSizeForCapacity(capacity));+ line->count = 0;+ line->capacity = capacity;+ + return line;+}++static cpPolyline *+cpPolylineMake2(int capacity, cpVect a, cpVect b)+{+ cpPolyline *line = cpPolylineMake(capacity);+ line->count = 2;+ line->verts[0] = a;+ line->verts[1] = b;+ + return line;+}++static cpPolyline *+cpPolylineShrink(cpPolyline *line)+{+ line->capacity = line->count;+ return (cpPolyline*) cprealloc(line, cpPolylineSizeForCapacity(line->count));+}++void+cpPolylineFree(cpPolyline *line)+{+ cpfree(line);+}++// Grow the allocated memory for a polyline.+static cpPolyline *+cpPolylineGrow(cpPolyline *line, int count)+{+ line->count += count;+ + int capacity = line->capacity;+ while(line->count > capacity) capacity *= 2;+ + if(line->capacity < capacity){+ line->capacity = capacity;+ line = (cpPolyline*) cprealloc(line, cpPolylineSizeForCapacity(capacity));+ }+ + return line;+}++// Push v onto the end of line.+static cpPolyline *+cpPolylinePush(cpPolyline *line, cpVect v)+{+ int count = line->count;+ line = cpPolylineGrow(line, 1);+ line->verts[count] = v;+ + return line;+}++// Push v onto the beginning of line.+static cpPolyline *+cpPolylineEnqueue(cpPolyline *line, cpVect v)+{+ // TODO could optimize this to grow in both directions.+ // Probably doesn't matter though.+ int count = line->count;+ line = cpPolylineGrow(line, 1);+ memmove(line->verts + 1, line->verts, count*sizeof(cpVect));+ line->verts[0] = v;+ + return line;+}++// Returns true if the polyline starts and ends with the same vertex.+cpBool+cpPolylineIsClosed(cpPolyline *line)+{+ return (line->count > 1 && cpveql(line->verts[0], line->verts[line->count-1]));+}++// Check if a cpPolyline is longer than a certain length+// Takes a range which can wrap around if the polyline is looped.+static cpBool+cpPolylineIsShort(cpVect *points, int count, int start, int end, cpFloat min)+{+ cpFloat length = 0.0f;+ for(int i=start; i!=end; i=Next(i, count)){+ length += cpvdist(points[i], points[Next(i, count)]);+ if(length > min) return cpFalse;+ }+ + return cpTrue;+}++//MARK: Polyline Simplification++static inline cpFloat+Sharpness(cpVect a, cpVect b, cpVect c)+{+ // TODO could speed this up by caching the normals instead of calculating each twice.+ return cpvdot(cpvnormalize(cpvsub(a, b)), cpvnormalize(cpvsub(c, b)));+}++// Join similar adjacent line segments together. Works well for hard edged shapes.+// 'tol' is the minimum anglular difference in radians of a vertex.+cpPolyline *+cpPolylineSimplifyVertexes(cpPolyline *line, cpFloat tol)+{+ cpPolyline *reduced = cpPolylineMake2(0, line->verts[0], line->verts[1]);+ + cpFloat minSharp = -cpfcos(tol);+ + for(int i=2; i<line->count; i++){+ cpVect vert = line->verts[i];+ cpFloat sharp = Sharpness(reduced->verts[reduced->count - 2], reduced->verts[reduced->count - 1], vert);+ + if(sharp <= minSharp){+ reduced->verts[reduced->count - 1] = vert;+ } else {+ reduced = cpPolylinePush(reduced, vert);+ }+ }+ + if(+ cpPolylineIsClosed(line) &&+ Sharpness(reduced->verts[reduced->count - 2], reduced->verts[0], reduced->verts[1]) < minSharp+ ){+ reduced->verts[0] = reduced->verts[reduced->count - 2];+ reduced->count--;+ }+ + // TODO shrink+ return reduced;+}++// Recursive function used by cpPolylineSimplifyCurves().+static cpPolyline *+DouglasPeucker(+ cpVect *verts, cpPolyline *reduced,+ int length, int start, int end,+ cpFloat min, cpFloat tol+){+ // Early exit if the points are adjacent+ if((end - start + length)%length < 2) return reduced;+ + cpVect a = verts[start];+ cpVect b = verts[end];+ + // Check if the length is below the threshold+ if(cpvnear(a, b, min) && cpPolylineIsShort(verts, length, start, end, min)) return reduced;+ + // Find the maximal vertex to split and recurse on+ cpFloat max = 0.0;+ int maxi = start;+ + cpVect n = cpvnormalize(cpvperp(cpvsub(b, a)));+ cpFloat d = cpvdot(n, a);+ + for(int i=Next(start, length); i!=end; i=Next(i, length)){+ cpFloat dist = fabs(cpvdot(n, verts[i]) - d);+ + if(dist > max){+ max = dist;+ maxi = i;+ }+ }+ + if(max > tol){+ reduced = DouglasPeucker(verts, reduced, length, start, maxi, min, tol);+ reduced = cpPolylinePush(reduced, verts[maxi]);+ reduced = DouglasPeucker(verts, reduced, length, maxi, end, min, tol);+ }+ + return reduced;+}++// Recursively reduce the vertex count on a polyline. Works best for smooth shapes.+// 'tol' is the maximum error for the reduction.+// The reduced polyline will never be farther than this distance from the original polyline.+cpPolyline *+cpPolylineSimplifyCurves(cpPolyline *line, cpFloat tol)+{+ cpPolyline *reduced = cpPolylineMake(line->count);+ + cpFloat min = tol/2.0f;+ + if(cpPolylineIsClosed(line)){+ int start, end;+ cpLoopIndexes(line->verts, line->count - 1, &start, &end);+ + reduced = cpPolylinePush(reduced, line->verts[start]);+ reduced = DouglasPeucker(line->verts, reduced, line->count - 1, start, end, min, tol);+ reduced = cpPolylinePush(reduced, line->verts[end]);+ reduced = DouglasPeucker(line->verts, reduced, line->count - 1, end, start, min, tol);+ reduced = cpPolylinePush(reduced, line->verts[start]);+ } else {+ reduced = cpPolylinePush(reduced, line->verts[0]);+ reduced = DouglasPeucker(line->verts, reduced, line->count, 0, line->count - 1, min, tol);+ reduced = cpPolylinePush(reduced, line->verts[line->count - 1]);+ }+ + return cpPolylineShrink(reduced);+}++//MARK: Polyline Sets++cpPolylineSet *+cpPolylineSetAlloc(void)+{+ return (cpPolylineSet *)cpcalloc(1, sizeof(cpPolylineSet));+}++cpPolylineSet *+cpPolylineSetInit(cpPolylineSet *set)+{+ set->count = 0;+ set->capacity = 8;+ set->lines = (cpPolyline**) cpcalloc(set->capacity, sizeof(cpPolyline));+ + return set;+}+++cpPolylineSet *+cpPolylineSetNew(void)+{+ return cpPolylineSetInit(cpPolylineSetAlloc());+}++void+cpPolylineSetDestroy(cpPolylineSet *set, cpBool freePolylines)+{+ if(freePolylines){+ for(int i=0; i<set->count; i++){+ cpPolylineFree(set->lines[i]);+ }+ }+ + cpfree(set->lines);+}+++void+cpPolylineSetFree(cpPolylineSet *set, cpBool freePolylines)+{+ if(set){+ cpPolylineSetDestroy(set, freePolylines);+ cpfree(set);+ }+}++// Find the polyline that ends with v.+static int+cpPolylineSetFindEnds(cpPolylineSet *set, cpVect v){+ int count = set->count;+ cpPolyline **lines = set->lines;+ + for(int i=0; i<count; i++){+ cpPolyline *line = lines[i];+ if(cpveql(line->verts[line->count - 1], v)) return i;+ }+ + return -1;+}++// Find the polyline that starts with v.+static int+cpPolylineSetFindStarts(cpPolylineSet *set, cpVect v){+ int count = set->count;+ cpPolyline **lines = set->lines;+ + for(int i=0; i<count; i++){+ if(cpveql(lines[i]->verts[0], v)) return i;+ }+ + return -1;+}++// Add a new polyline to a polyline set.+static void+cpPolylineSetPush(cpPolylineSet *set, cpPolyline *line)+{+ // grow set+ set->count++;+ if(set->count > set->capacity){+ set->capacity *= 2;+ set->lines = (cpPolyline**) cprealloc(set->lines, set->capacity*sizeof(cpPolyline));+ }+ + set->lines[set->count - 1] = line;+}++// Add a new polyline to a polyline set.+static void+cpPolylineSetAdd(cpPolylineSet *set, cpVect v0, cpVect v1)+{+ cpPolylineSetPush(set, cpPolylineMake2(DEFAULT_POLYLINE_CAPACITY, v0, v1));+}++// Join two cpPolylines in a polyline set together.+static void+cpPolylineSetJoin(cpPolylineSet *set, int before, int after)+{+ cpPolyline *lbefore = set->lines[before];+ cpPolyline *lafter = set->lines[after];+ + // append+ int count = lbefore->count;+ lbefore = cpPolylineGrow(lbefore, lafter->count);+ memmove(lbefore->verts + count, lafter->verts, lafter->count*sizeof(cpVect));+ set->lines[before] = lbefore;+ + // delete lafter+ set->count--;+ cpPolylineFree(set->lines[after]);+ set->lines[after] = set->lines[set->count];+}++// Add a segment to a polyline set.+// A segment will either start a new polyline, join two others, or add to or loop an existing polyline.+void+cpPolylineSetCollectSegment(cpVect v0, cpVect v1, cpPolylineSet *lines)+{+ int before = cpPolylineSetFindEnds(lines, v0);+ int after = cpPolylineSetFindStarts(lines, v1);+ + if(before >= 0 && after >= 0){+ if(before == after){+ // loop by pushing v1 onto before+ lines->lines[before] = cpPolylinePush(lines->lines[before], v1);+ } else {+ // join before and after+ cpPolylineSetJoin(lines, before, after);+ }+ } else if(before >= 0){+ // push v1 onto before+ lines->lines[before] = cpPolylinePush(lines->lines[before], v1);+ } else if(after >= 0){+ // enqueue v0 onto after+ lines->lines[after] = cpPolylineEnqueue(lines->lines[after], v0);+ } else {+ // create new line from v0 and v1+ cpPolylineSetAdd(lines, v0, v1);+ }+}++//MARK: Convex Hull Functions++cpPolyline *+cpPolylineToConvexHull(cpPolyline *line, cpFloat tol)+{+ cpPolyline *hull = cpPolylineMake(line->count + 1);+ hull->count = cpConvexHull(line->count, line->verts, hull->verts, NULL, tol);+ hull = cpPolylinePush(hull, hull->verts[0]);+ + return cpPolylineShrink(hull);+}++//MARK: Approximate Concave Decompostition++struct Notch {+ int i;+ cpFloat d;+ cpVect v;+ cpVect n;+};++static cpFloat+FindSteiner(int count, cpVect *verts, struct Notch notch)+{+ cpFloat min = INFINITY;+ cpFloat feature = -1.0;+ + for(int i=1; i<count-1; i++){+ int index = (notch.i + i)%count;+ + cpVect seg_a = verts[index];+ cpVect seg_b = verts[Next(index, count)];+ + cpFloat thing_a = cpvcross(notch.n, cpvsub(seg_a, notch.v));+ cpFloat thing_b = cpvcross(notch.n, cpvsub(seg_b, notch.v));+ if(thing_a*thing_b <= 0.0){+ cpFloat t = thing_a/(thing_a - thing_b);+ cpFloat dist = cpvdot(notch.n, cpvsub(cpvlerp(seg_a, seg_b, t), notch.v));+ + if(dist >= 0.0 && dist <= min){+ min = dist;+ feature = index + t;+ }+ }+ }+ + return feature;+}++//static cpFloat+//FindSteiner2(cpVect *verts, int count, struct Notch notch)+//{+// cpVect a = verts[(notch.i + count - 1)%count];+// cpVect b = verts[(notch.i + 1)%count];+// cpVect n = cpvnormalize(cpvadd(cpvnormalize(cpvsub(notch.v, a)), cpvnormalize(cpvsub(notch.v, b))));+// +// cpFloat min = INFINITY;+// cpFloat feature = -1.0;+// +// for(int i=1; i<count-1; i++){+// int index = (notch.i + i)%count;+// +// cpVect seg_a = verts[index];+// cpVect seg_b = verts[Next(index, count)];+// +// cpFloat thing_a = cpvcross(n, cpvsub(seg_a, notch.v));+// cpFloat thing_b = cpvcross(n, cpvsub(seg_b, notch.v));+// if(thing_a*thing_b <= 0.0){+// cpFloat t = thing_a/(thing_a - thing_b);+// cpFloat dist = cpvdot(n, cpvsub(cpvlerp(seg_a, seg_b, t), notch.v));+// +// if(dist >= 0.0 && dist <= min){+// min = dist;+// feature = index + t;+// }+// }+// }+// +// cpAssertSoft(feature >= 0.0, "No closest features detected. This is likely due to a self intersecting polygon.");+// return feature;+//}++//struct Range {cpFloat min, max;};+//static inline struct Range+//clip_range(cpVect delta_a, cpVect delta_b, cpVect clip)+//{+// cpFloat da = cpvcross(delta_a, clip);+// cpFloat db = cpvcross(delta_b, clip);+// cpFloat clamp = da/(da - db);+// if(da > db){+// return (struct Range){-INFINITY, clamp};+// } else if(da < db){+// return (struct Range){clamp, INFINITY};+// } else {+// return (struct Range){-INFINITY, INFINITY};+// }+//}+//+//static cpFloat+//FindSteiner3(cpVect *verts, int count, struct Notch notch)+//{+// cpFloat min = INFINITY;+// cpFloat feature = -1.0;+// +// cpVect support_a = verts[(notch.i - 1 + count)%count];+// cpVect support_b = verts[(notch.i + 1)%count];+// +// cpVect clip_a = cpvlerp(support_a, support_b, 0.1);+// cpVect clip_b = cpvlerp(support_b, support_b, 0.9);+// +// for(int i=1; i<count - 1; i++){+// int index = (notch.i + i)%count;+// cpVect seg_a = verts[index];+// cpVect seg_b = verts[Next(index, count)];+// +// cpVect delta_a = cpvsub(seg_a, notch.v);+// cpVect delta_b = cpvsub(seg_b, notch.v);+// +// // Ignore if the segment faces away from the point.+// if(cpvcross(delta_b, delta_a) > 0.0){+// struct Range range1 = clip_range(delta_a, delta_b, cpvsub(notch.v, clip_a));+// struct Range range2 = clip_range(delta_a, delta_b, cpvsub(clip_b, notch.v));+// +// cpFloat min_t = cpfmax(0.0, cpfmax(range1.min, range2.min));+// cpFloat max_t = cpfmin(1.0, cpfmin(range1.max, range2.max));+// +// // Ignore if the segment has been completely clipped away.+// if(min_t < max_t){+// cpVect seg_delta = cpvsub(seg_b, seg_a);+// cpFloat closest_t = cpfclamp(cpvdot(seg_delta, cpvsub(notch.v, seg_a))/cpvlengthsq(seg_delta), min_t, max_t);+// cpVect closest = cpvlerp(seg_a, seg_b, closest_t);+// +// cpFloat dist = cpvdistsq(notch.v, closest);+// if(dist < min){+// min = dist;+// feature = index + closest_t;+// }+// }+// }+// }+// +// cpAssertWarn(feature >= 0.0, "Internal Error: No closest features detected.");+// return feature;+//}++//static cpBool+//VertexUnobscured(int count, cpVect *verts, int index, int notch_i)+//{+// cpVect v = verts[notch_i];+// cpVect n = cpvnormalize(cpvsub(verts[index], v));+// +// for(int i=0; i<count; i++){+// if(i == index || i == Next(i, count) || i == notch_i || i == Next(notch_i, count)) continue;+// +// cpVect seg_a = verts[i];+// cpVect seg_b = verts[Next(i, count)];+// +// cpFloat thing_a = cpvcross(n, cpvsub(seg_a, v));+// cpFloat thing_b = cpvcross(n, cpvsub(seg_b, v));+// if(thing_a*thing_b <= 0.0) return cpTrue;+// }+// +// return cpFalse;+//}+//+//static cpFloat+//FindSteiner4(int count, cpVect *verts, struct Notch notch, cpFloat *convexity)+//{+// cpFloat min = INFINITY;+// cpFloat feature = -1.0;+// +// for(int i=Next(notch.b, count); i!=notch.a; i=Next(i, count)){+// cpVect v = verts[i];+// cpFloat weight = (1.0 + 0.1*convexity[i])/(1.0*cpvdist(notch.v, v));+// +// if(weight <= min && VertexUnobscured(count, verts, i, notch.i)){+// min = weight;+// feature = i;+// }+// }+// +// cpAssertSoft(feature >= 0.0, "No closest features detected. This is likely due to a self intersecting polygon.");+// return feature;+//}++static struct Notch+DeepestNotch(int count, cpVect *verts, int hullCount, cpVect *hullVerts, int first, cpFloat tol)+{+ struct Notch notch = {};+ int j = Next(first, count);+ + for(int i=0; i<hullCount; i++){+ cpVect a = hullVerts[i];+ cpVect b = hullVerts[Next(i, hullCount)];+ + // TODO use a cross check instead?+ cpVect n = cpvnormalize(cpvrperp(cpvsub(a, b)));+ cpFloat d = cpvdot(n, a);+ + cpVect v = verts[j];+ while(!cpveql(v, b)){+ cpFloat depth = cpvdot(n, v) - d;+ + if(depth > notch.d){+ notch.d = depth;+ notch.i = j;+ notch.v = v;+ notch.n = n;+ }+ + j = Next(j, count);+ v = verts[j];+ }+ + j = Next(j, count);+ }+ + return notch;+}++static inline int IMAX(int a, int b){return (a > b ? a : b);}++static void+ApproximateConcaveDecomposition(cpVect *verts, int count, cpFloat tol, cpPolylineSet *set)+{+ int first;+ cpVect *hullVerts = (cpVect*) alloca(count*sizeof(cpVect));+ int hullCount = cpConvexHull(count, verts, hullVerts, &first, 0.0);+ + if(hullCount != count){+ struct Notch notch = DeepestNotch(count, verts, hullCount, hullVerts, first, tol);+ + if(notch.d > tol){+ cpFloat steiner_it = FindSteiner(count, verts, notch);+ + if(steiner_it >= 0.0){+ int steiner_i = (int)steiner_it;+ cpVect steiner = cpvlerp(verts[steiner_i], verts[Next(steiner_i, count)], steiner_it - steiner_i);+ + // Vertex counts NOT including the steiner point.+ int sub1_count = (steiner_i - notch.i + count)%count + 1;+ int sub2_count = count - (steiner_i - notch.i + count)%count;+ cpVect *scratch = (cpVect*) alloca((IMAX(sub1_count, sub2_count) + 1)*sizeof(cpVect));+ + for(int i=0; i<sub1_count; i++) scratch[i] = verts[(notch.i + i)%count];+ scratch[sub1_count] = steiner;+ ApproximateConcaveDecomposition(scratch, sub1_count + 1, tol, set);+ + for(int i=0; i<sub2_count; i++) scratch[i] = verts[(steiner_i + 1 + i)%count];+ scratch[sub2_count] = steiner;+ ApproximateConcaveDecomposition(scratch, sub2_count + 1, tol, set);+ + return;+ }+ }+ }+ + cpPolyline *hull = cpPolylineMake(hullCount + 1);+ + memcpy(hull->verts, hullVerts, hullCount*sizeof(cpVect));+ hull->verts[hullCount] = hullVerts[0];+ hull->count = hullCount + 1;+ + cpPolylineSetPush(set, hull);+}++cpPolylineSet *+cpPolylineConvexDecomposition_BETA(cpPolyline *line, cpFloat tol)+{+ cpAssertSoft(cpPolylineIsClosed(line), "Cannot decompose an open polygon.");+ cpAssertSoft(cpAreaForPoly(line->count, line->verts, 0.0) >= 0.0, "Winding is backwards. (Are you passing a hole?)");+ + cpPolylineSet *set = cpPolylineSetNew();+ ApproximateConcaveDecomposition(line->verts, line->count - 1, tol, set);+ + return set;+}
+ Chipmunk2D-7.0.2/src/cpRatchetJoint.c view
@@ -0,0 +1,179 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++static void+preStep(cpRatchetJoint *joint, cpFloat dt)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + cpFloat angle = joint->angle;+ cpFloat phase = joint->phase;+ cpFloat ratchet = joint->ratchet;+ + cpFloat delta = b->a - a->a;+ cpFloat diff = angle - delta;+ cpFloat pdist = 0.0f;+ + if(diff*ratchet > 0.0f){+ pdist = diff;+ } else {+ joint->angle = cpffloor((delta - phase)/ratchet)*ratchet + phase;+ }+ + // calculate moment of inertia coefficient.+ joint->iSum = 1.0f/(a->i_inv + b->i_inv);+ + // calculate bias velocity+ cpFloat maxBias = joint->constraint.maxBias;+ joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*pdist/dt, -maxBias, maxBias);++ // If the bias is 0, the joint is not at a limit. Reset the impulse.+ if(!joint->bias) joint->jAcc = 0.0f;+}++static void+applyCachedImpulse(cpRatchetJoint *joint, cpFloat dt_coef)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + cpFloat j = joint->jAcc*dt_coef;+ a->w -= j*a->i_inv;+ b->w += j*b->i_inv;+}++static void+applyImpulse(cpRatchetJoint *joint, cpFloat dt)+{+ if(!joint->bias) return; // early exit++ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + // compute relative rotational velocity+ cpFloat wr = b->w - a->w;+ cpFloat ratchet = joint->ratchet;+ + cpFloat jMax = joint->constraint.maxForce*dt;+ + // compute normal impulse + cpFloat j = -(joint->bias + wr)*joint->iSum;+ cpFloat jOld = joint->jAcc;+ joint->jAcc = cpfclamp((jOld + j)*ratchet, 0.0f, jMax*cpfabs(ratchet))/ratchet;+ j = joint->jAcc - jOld;+ + // apply impulse+ a->w -= j*a->i_inv;+ b->w += j*b->i_inv;+}++static cpFloat+getImpulse(cpRatchetJoint *joint)+{+ return cpfabs(joint->jAcc);+}++static const cpConstraintClass klass = {+ (cpConstraintPreStepImpl)preStep,+ (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,+ (cpConstraintApplyImpulseImpl)applyImpulse,+ (cpConstraintGetImpulseImpl)getImpulse,+};++cpRatchetJoint *+cpRatchetJointAlloc(void)+{+ return (cpRatchetJoint *)cpcalloc(1, sizeof(cpRatchetJoint));+}++cpRatchetJoint *+cpRatchetJointInit(cpRatchetJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet)+{+ cpConstraintInit((cpConstraint *)joint, &klass, a, b);+ + joint->angle = 0.0f;+ joint->phase = phase;+ joint->ratchet = ratchet;+ + // STATIC_BODY_CHECK+ joint->angle = (b ? b->a : 0.0f) - (a ? a->a : 0.0f);+ + return joint;+}++cpConstraint *+cpRatchetJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet)+{+ return (cpConstraint *)cpRatchetJointInit(cpRatchetJointAlloc(), a, b, phase, ratchet);+}++cpBool+cpConstraintIsRatchetJoint(const cpConstraint *constraint)+{+ return (constraint->klass == &klass);+}++cpFloat+cpRatchetJointGetAngle(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint.");+ return ((cpRatchetJoint *)constraint)->angle;+}++void+cpRatchetJointSetAngle(cpConstraint *constraint, cpFloat angle)+{+ cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint.");+ cpConstraintActivateBodies(constraint);+ ((cpRatchetJoint *)constraint)->angle = angle;+}++cpFloat+cpRatchetJointGetPhase(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint.");+ return ((cpRatchetJoint *)constraint)->phase;+}++void+cpRatchetJointSetPhase(cpConstraint *constraint, cpFloat phase)+{+ cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint.");+ cpConstraintActivateBodies(constraint);+ ((cpRatchetJoint *)constraint)->phase = phase;+}+cpFloat+cpRatchetJointGetRatchet(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint.");+ return ((cpRatchetJoint *)constraint)->ratchet;+}++void+cpRatchetJointSetRatchet(cpConstraint *constraint, cpFloat ratchet)+{+ cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint.");+ cpConstraintActivateBodies(constraint);+ ((cpRatchetJoint *)constraint)->ratchet = ratchet;+}
+ Chipmunk2D-7.0.2/src/cpRobust.c view
@@ -0,0 +1,13 @@+#include "chipmunk/cpRobust.h"+++cpBool+cpCheckPointGreater(const cpVect a, const cpVect b, const cpVect c)+{+ return (b.y - a.y)*(a.x + b.x - 2*c.x) > (b.x - a.x)*(a.y + b.y - 2*c.y);+}++cpBool+cpCheckAxis(cpVect v0, cpVect v1, cpVect p, cpVect n){+ return cpvdot(p, n) <= cpfmax(cpvdot(v0, n), cpvdot(v1, n));+}
+ Chipmunk2D-7.0.2/src/cpRotaryLimitJoint.c view
@@ -0,0 +1,160 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++static void+preStep(cpRotaryLimitJoint *joint, cpFloat dt)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + cpFloat dist = b->a - a->a;+ cpFloat pdist = 0.0f;+ if(dist > joint->max) {+ pdist = joint->max - dist;+ } else if(dist < joint->min) {+ pdist = joint->min - dist;+ }+ + // calculate moment of inertia coefficient.+ joint->iSum = 1.0f/(a->i_inv + b->i_inv);+ + // calculate bias velocity+ cpFloat maxBias = joint->constraint.maxBias;+ joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*pdist/dt, -maxBias, maxBias);++ // If the bias is 0, the joint is not at a limit. Reset the impulse.+ if(!joint->bias) joint->jAcc = 0.0f;+}++static void+applyCachedImpulse(cpRotaryLimitJoint *joint, cpFloat dt_coef)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + cpFloat j = joint->jAcc*dt_coef;+ a->w -= j*a->i_inv;+ b->w += j*b->i_inv;+}++static void+applyImpulse(cpRotaryLimitJoint *joint, cpFloat dt)+{+ if(!joint->bias) return; // early exit++ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + // compute relative rotational velocity+ cpFloat wr = b->w - a->w;+ + cpFloat jMax = joint->constraint.maxForce*dt;+ + // compute normal impulse + cpFloat j = -(joint->bias + wr)*joint->iSum;+ cpFloat jOld = joint->jAcc;+ if(joint->bias < 0.0f){+ joint->jAcc = cpfclamp(jOld + j, 0.0f, jMax);+ } else {+ joint->jAcc = cpfclamp(jOld + j, -jMax, 0.0f);+ }+ j = joint->jAcc - jOld;+ + // apply impulse+ a->w -= j*a->i_inv;+ b->w += j*b->i_inv;+}++static cpFloat+getImpulse(cpRotaryLimitJoint *joint)+{+ return cpfabs(joint->jAcc);+}++static const cpConstraintClass klass = {+ (cpConstraintPreStepImpl)preStep,+ (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,+ (cpConstraintApplyImpulseImpl)applyImpulse,+ (cpConstraintGetImpulseImpl)getImpulse,+};++cpRotaryLimitJoint *+cpRotaryLimitJointAlloc(void)+{+ return (cpRotaryLimitJoint *)cpcalloc(1, sizeof(cpRotaryLimitJoint));+}++cpRotaryLimitJoint *+cpRotaryLimitJointInit(cpRotaryLimitJoint *joint, cpBody *a, cpBody *b, cpFloat min, cpFloat max)+{+ cpConstraintInit((cpConstraint *)joint, &klass, a, b);+ + joint->min = min;+ joint->max = max;+ + joint->jAcc = 0.0f;+ + return joint;+}++cpConstraint *+cpRotaryLimitJointNew(cpBody *a, cpBody *b, cpFloat min, cpFloat max)+{+ return (cpConstraint *)cpRotaryLimitJointInit(cpRotaryLimitJointAlloc(), a, b, min, max);+}++cpBool+cpConstraintIsRotaryLimitJoint(const cpConstraint *constraint)+{+ return (constraint->klass == &klass);+}++cpFloat+cpRotaryLimitJointGetMin(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint.");+ return ((cpRotaryLimitJoint *)constraint)->min;+}++void+cpRotaryLimitJointSetMin(cpConstraint *constraint, cpFloat min)+{+ cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint.");+ cpConstraintActivateBodies(constraint);+ ((cpRotaryLimitJoint *)constraint)->min = min;+}++cpFloat+cpRotaryLimitJointGetMax(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint.");+ return ((cpRotaryLimitJoint *)constraint)->max;+}++void+cpRotaryLimitJointSetMax(cpConstraint *constraint, cpFloat max)+{+ cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint.");+ cpConstraintActivateBodies(constraint);+ ((cpRotaryLimitJoint *)constraint)->max = max;+}
+ Chipmunk2D-7.0.2/src/cpShape.c view
@@ -0,0 +1,603 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"+#include "chipmunk/chipmunk_unsafe.h"++#define CP_DefineShapeGetter(struct, type, member, name) \+CP_DeclareShapeGetter(struct, type, name){ \+ cpAssertHard(shape->klass == &struct##Class, "shape is not a "#struct); \+ return ((struct *)shape)->member; \+}++cpShape *+cpShapeInit(cpShape *shape, const cpShapeClass *klass, cpBody *body, struct cpShapeMassInfo massInfo)+{+ shape->klass = klass;+ + shape->body = body;+ shape->massInfo = massInfo;+ + shape->sensor = 0;+ + shape->e = 0.0f;+ shape->u = 0.0f;+ shape->surfaceV = cpvzero;+ + shape->type = 0;+ shape->filter.group = CP_NO_GROUP;+ shape->filter.categories = CP_ALL_CATEGORIES;+ shape->filter.mask = CP_ALL_CATEGORIES;+ + shape->userData = NULL;+ + shape->space = NULL;+ + shape->next = NULL;+ shape->prev = NULL;+ + return shape;+}++void+cpShapeDestroy(cpShape *shape)+{+ if(shape->klass && shape->klass->destroy) shape->klass->destroy(shape);+}++void+cpShapeFree(cpShape *shape)+{+ if(shape){+ cpShapeDestroy(shape);+ cpfree(shape);+ }+}++cpSpace *+cpShapeGetSpace(const cpShape *shape)+{+ return shape->space;+}++cpBody *+cpShapeGetBody(const cpShape *shape)+{+ return shape->body;+}++void+cpShapeSetBody(cpShape *shape, cpBody *body)+{+ cpAssertHard(!cpShapeActive(shape), "You cannot change the body on an active shape. You must remove the shape from the space before changing the body.");+ shape->body = body;+}++cpFloat cpShapeGetMass(cpShape *shape){ return shape->massInfo.m; }++void+cpShapeSetMass(cpShape *shape, cpFloat mass){+ cpBody *body = shape->body;+ cpBodyActivate(body);+ + shape->massInfo.m = mass;+ cpBodyAccumulateMassFromShapes(body);+}++cpFloat cpShapeGetDensity(cpShape *shape){ return shape->massInfo.m/shape->massInfo.area; }+void cpShapeSetDensity(cpShape *shape, cpFloat density){ cpShapeSetMass(shape, density*shape->massInfo.area); }++cpFloat cpShapeGetMoment(cpShape *shape){ return shape->massInfo.m*shape->massInfo.i; }+cpFloat cpShapeGetArea(cpShape *shape){ return shape->massInfo.area; }+cpVect cpShapeGetCenterOfGravity(cpShape *shape) { return shape->massInfo.cog; }++cpBB+cpShapeGetBB(const cpShape *shape)+{+ return shape->bb;+}++cpBool+cpShapeGetSensor(const cpShape *shape)+{+ return shape->sensor;+}++void+cpShapeSetSensor(cpShape *shape, cpBool sensor)+{+ cpBodyActivate(shape->body);+ shape->sensor = sensor;+}++cpFloat+cpShapeGetElasticity(const cpShape *shape)+{+ return shape->e;+}++void+cpShapeSetElasticity(cpShape *shape, cpFloat elasticity)+{+ cpAssertHard(elasticity >= 0.0f, "Elasticity must be positive.");+ cpBodyActivate(shape->body);+ shape->e = elasticity;+}++cpFloat+cpShapeGetFriction(const cpShape *shape)+{+ return shape->u;+}++void+cpShapeSetFriction(cpShape *shape, cpFloat friction)+{+ cpAssertHard(friction >= 0.0f, "Friction must be postive.");+ cpBodyActivate(shape->body);+ shape->u = friction;+}++cpVect+cpShapeGetSurfaceVelocity(const cpShape *shape)+{+ return shape->surfaceV;+}++void+cpShapeSetSurfaceVelocity(cpShape *shape, cpVect surfaceVelocity)+{+ cpBodyActivate(shape->body);+ shape->surfaceV = surfaceVelocity;+}++cpDataPointer+cpShapeGetUserData(const cpShape *shape)+{+ return shape->userData;+}++void+cpShapeSetUserData(cpShape *shape, cpDataPointer userData)+{+ shape->userData = userData;+}++cpCollisionType+cpShapeGetCollisionType(const cpShape *shape)+{+ return shape->type;+}++void+cpShapeSetCollisionType(cpShape *shape, cpCollisionType collisionType)+{+ cpBodyActivate(shape->body);+ shape->type = collisionType;+}++cpShapeFilter+cpShapeGetFilter(const cpShape *shape)+{+ return shape->filter;+}++void+cpShapeSetFilter(cpShape *shape, cpShapeFilter filter)+{+ cpBodyActivate(shape->body);+ shape->filter = filter;+}++cpBB+cpShapeCacheBB(cpShape *shape)+{+ return cpShapeUpdate(shape, shape->body->transform);+}++cpBB+cpShapeUpdate(cpShape *shape, cpTransform transform)+{+ return (shape->bb = shape->klass->cacheData(shape, transform));+}++cpFloat+cpShapePointQuery(const cpShape *shape, cpVect p, cpPointQueryInfo *info)+{+ cpPointQueryInfo blank = {NULL, cpvzero, INFINITY, cpvzero};+ if(info){+ (*info) = blank;+ } else {+ info = ␣+ }+ + shape->klass->pointQuery(shape, p, info);+ return info->distance;+}+++cpBool+cpShapeSegmentQuery(const cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info){+ cpSegmentQueryInfo blank = {NULL, b, cpvzero, 1.0f};+ if(info){+ (*info) = blank;+ } else {+ info = ␣+ }+ + cpPointQueryInfo nearest;+ shape->klass->pointQuery(shape, a, &nearest);+ if(nearest.distance <= radius){+ info->shape = shape;+ info->alpha = 0.0;+ info->normal = cpvnormalize(cpvsub(a, nearest.point));+ } else {+ shape->klass->segmentQuery(shape, a, b, radius, info);+ }+ + return (info->shape != NULL);+}++cpContactPointSet+cpShapesCollide(const cpShape *a, const cpShape *b)+{+ struct cpContact contacts[CP_MAX_CONTACTS_PER_ARBITER];+ struct cpCollisionInfo info = cpCollide(a, b, 0, contacts);+ + cpContactPointSet set;+ set.count = info.count;+ + // cpCollideShapes() may have swapped the contact order. Flip the normal.+ cpBool swapped = (a != info.a);+ set.normal = (swapped ? cpvneg(info.n) : info.n);+ + for(int i=0; i<info.count; i++){+ // cpCollideShapesInfo() returns contacts with absolute positions.+ cpVect p1 = contacts[i].r1;+ cpVect p2 = contacts[i].r2;+ + set.points[i].pointA = (swapped ? p2 : p1);+ set.points[i].pointB = (swapped ? p1 : p2);+ set.points[i].distance = cpvdot(cpvsub(p2, p1), set.normal);+ }+ + return set;+}++cpCircleShape *+cpCircleShapeAlloc(void)+{+ return (cpCircleShape *)cpcalloc(1, sizeof(cpCircleShape));+}++static cpBB+cpCircleShapeCacheData(cpCircleShape *circle, cpTransform transform)+{+ cpVect c = circle->tc = cpTransformPoint(transform, circle->c);+ return cpBBNewForCircle(c, circle->r);+}++static void+cpCircleShapePointQuery(cpCircleShape *circle, cpVect p, cpPointQueryInfo *info)+{+ cpVect delta = cpvsub(p, circle->tc);+ cpFloat d = cpvlength(delta);+ cpFloat r = circle->r;+ + info->shape = (cpShape *)circle;+ info->point = cpvadd(circle->tc, cpvmult(delta, r/d)); // TODO: div/0+ info->distance = d - r;+ + // Use up for the gradient if the distance is very small.+ info->gradient = (d > MAGIC_EPSILON ? cpvmult(delta, 1.0f/d) : cpv(0.0f, 1.0f));+}++static void+cpCircleShapeSegmentQuery(cpCircleShape *circle, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info)+{+ CircleSegmentQuery((cpShape *)circle, circle->tc, circle->r, a, b, radius, info);+}++static struct cpShapeMassInfo+cpCircleShapeMassInfo(cpFloat mass, cpFloat radius, cpVect center)+{+ struct cpShapeMassInfo info = {+ mass, cpMomentForCircle(1.0f, 0.0f, radius, cpvzero),+ center,+ cpAreaForCircle(0.0f, radius),+ };+ + return info;+}++static const cpShapeClass cpCircleShapeClass = {+ CP_CIRCLE_SHAPE,+ (cpShapeCacheDataImpl)cpCircleShapeCacheData,+ NULL,+ (cpShapePointQueryImpl)cpCircleShapePointQuery,+ (cpShapeSegmentQueryImpl)cpCircleShapeSegmentQuery,+};++cpCircleShape *+cpCircleShapeInit(cpCircleShape *circle, cpBody *body, cpFloat radius, cpVect offset)+{+ circle->c = offset;+ circle->r = radius;+ + cpShapeInit((cpShape *)circle, &cpCircleShapeClass, body, cpCircleShapeMassInfo(0.0f, radius, offset));+ + return circle;+}++cpShape *+cpCircleShapeNew(cpBody *body, cpFloat radius, cpVect offset)+{+ return (cpShape *)cpCircleShapeInit(cpCircleShapeAlloc(), body, radius, offset);+}++cpVect+cpCircleShapeGetOffset(const cpShape *shape)+{+ cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape.");+ return ((cpCircleShape *)shape)->c;+}++cpFloat+cpCircleShapeGetRadius(const cpShape *shape)+{+ cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape.");+ return ((cpCircleShape *)shape)->r;+}+++cpSegmentShape *+cpSegmentShapeAlloc(void)+{+ return (cpSegmentShape *)cpcalloc(1, sizeof(cpSegmentShape));+}++static cpBB+cpSegmentShapeCacheData(cpSegmentShape *seg, cpTransform transform)+{+ seg->ta = cpTransformPoint(transform, seg->a);+ seg->tb = cpTransformPoint(transform, seg->b);+ seg->tn = cpTransformVect(transform, seg->n);+ + cpFloat l,r,b,t;+ + if(seg->ta.x < seg->tb.x){+ l = seg->ta.x;+ r = seg->tb.x;+ } else {+ l = seg->tb.x;+ r = seg->ta.x;+ }+ + if(seg->ta.y < seg->tb.y){+ b = seg->ta.y;+ t = seg->tb.y;+ } else {+ b = seg->tb.y;+ t = seg->ta.y;+ }+ + cpFloat rad = seg->r;+ return cpBBNew(l - rad, b - rad, r + rad, t + rad);+}++static void+cpSegmentShapePointQuery(cpSegmentShape *seg, cpVect p, cpPointQueryInfo *info)+{+ cpVect closest = cpClosetPointOnSegment(p, seg->ta, seg->tb);+ + cpVect delta = cpvsub(p, closest);+ cpFloat d = cpvlength(delta);+ cpFloat r = seg->r;+ cpVect g = cpvmult(delta, 1.0f/d);+ + info->shape = (cpShape *)seg;+ info->point = (d ? cpvadd(closest, cpvmult(g, r)) : closest);+ info->distance = d - r;+ + // Use the segment's normal if the distance is very small.+ info->gradient = (d > MAGIC_EPSILON ? g : seg->n);+}++static void+cpSegmentShapeSegmentQuery(cpSegmentShape *seg, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info)+{+ cpVect n = seg->tn;+ cpFloat d = cpvdot(cpvsub(seg->ta, a), n);+ cpFloat r = seg->r + r2;+ + cpVect flipped_n = (d > 0.0f ? cpvneg(n) : n);+ cpVect seg_offset = cpvsub(cpvmult(flipped_n, r), a);+ + // Make the endpoints relative to 'a' and move them by the thickness of the segment.+ cpVect seg_a = cpvadd(seg->ta, seg_offset);+ cpVect seg_b = cpvadd(seg->tb, seg_offset);+ cpVect delta = cpvsub(b, a);+ + if(cpvcross(delta, seg_a)*cpvcross(delta, seg_b) <= 0.0f){+ cpFloat d_offset = d + (d > 0.0f ? -r : r);+ cpFloat ad = -d_offset;+ cpFloat bd = cpvdot(delta, n) - d_offset;+ + if(ad*bd < 0.0f){+ cpFloat t = ad/(ad - bd);+ + info->shape = (cpShape *)seg;+ info->point = cpvsub(cpvlerp(a, b, t), cpvmult(flipped_n, r2));+ info->normal = flipped_n;+ info->alpha = t;+ }+ } else if(r != 0.0f){+ cpSegmentQueryInfo info1 = {NULL, b, cpvzero, 1.0f};+ cpSegmentQueryInfo info2 = {NULL, b, cpvzero, 1.0f};+ CircleSegmentQuery((cpShape *)seg, seg->ta, seg->r, a, b, r2, &info1);+ CircleSegmentQuery((cpShape *)seg, seg->tb, seg->r, a, b, r2, &info2);+ + if(info1.alpha < info2.alpha){+ (*info) = info1;+ } else {+ (*info) = info2;+ }+ }+}++static struct cpShapeMassInfo+cpSegmentShapeMassInfo(cpFloat mass, cpVect a, cpVect b, cpFloat r)+{+ struct cpShapeMassInfo info = {+ mass, cpMomentForBox(1.0f, cpvdist(a, b) + 2.0f*r, 2.0f*r), // TODO is an approximation.+ cpvlerp(a, b, 0.5f),+ cpAreaForSegment(a, b, r),+ };+ + return info;+}++static const cpShapeClass cpSegmentShapeClass = {+ CP_SEGMENT_SHAPE,+ (cpShapeCacheDataImpl)cpSegmentShapeCacheData,+ NULL,+ (cpShapePointQueryImpl)cpSegmentShapePointQuery,+ (cpShapeSegmentQueryImpl)cpSegmentShapeSegmentQuery,+};++cpSegmentShape *+cpSegmentShapeInit(cpSegmentShape *seg, cpBody *body, cpVect a, cpVect b, cpFloat r)+{+ seg->a = a;+ seg->b = b;+ seg->n = cpvrperp(cpvnormalize(cpvsub(b, a)));+ + seg->r = r;+ + seg->a_tangent = cpvzero;+ seg->b_tangent = cpvzero;+ + cpShapeInit((cpShape *)seg, &cpSegmentShapeClass, body, cpSegmentShapeMassInfo(0.0f, a, b, r));+ + return seg;+}++cpShape*+cpSegmentShapeNew(cpBody *body, cpVect a, cpVect b, cpFloat r)+{+ return (cpShape *)cpSegmentShapeInit(cpSegmentShapeAlloc(), body, a, b, r);+}++cpVect+cpSegmentShapeGetA(const cpShape *shape)+{+ cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");+ return ((cpSegmentShape *)shape)->a;+}++cpVect+cpSegmentShapeGetB(const cpShape *shape)+{+ cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");+ return ((cpSegmentShape *)shape)->b;+}++cpVect+cpSegmentShapeGetNormal(const cpShape *shape)+{+ cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");+ return ((cpSegmentShape *)shape)->n;+}++cpFloat+cpSegmentShapeGetRadius(const cpShape *shape)+{+ cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");+ return ((cpSegmentShape *)shape)->r;+}++void+cpSegmentShapeSetNeighbors(cpShape *shape, cpVect prev, cpVect next)+{+ cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");+ cpSegmentShape *seg = (cpSegmentShape *)shape;+ + seg->a_tangent = cpvsub(prev, seg->a);+ seg->b_tangent = cpvsub(next, seg->b);+}++// Unsafe API (chipmunk_unsafe.h)++// TODO setters should wake the shape up?++void+cpCircleShapeSetRadius(cpShape *shape, cpFloat radius)+{+ cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape.");+ cpCircleShape *circle = (cpCircleShape *)shape;+ + circle->r = radius;+ + cpFloat mass = shape->massInfo.m;+ shape->massInfo = cpCircleShapeMassInfo(mass, circle->r, circle->c);+ if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);+}++void+cpCircleShapeSetOffset(cpShape *shape, cpVect offset)+{+ cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape.");+ cpCircleShape *circle = (cpCircleShape *)shape;+ + circle->c = offset;++ cpFloat mass = shape->massInfo.m;+ shape->massInfo = cpCircleShapeMassInfo(shape->massInfo.m, circle->r, circle->c);+ if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);+}++void+cpSegmentShapeSetEndpoints(cpShape *shape, cpVect a, cpVect b)+{+ cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");+ cpSegmentShape *seg = (cpSegmentShape *)shape;+ + seg->a = a;+ seg->b = b;+ seg->n = cpvperp(cpvnormalize(cpvsub(b, a)));++ cpFloat mass = shape->massInfo.m;+ shape->massInfo = cpSegmentShapeMassInfo(shape->massInfo.m, seg->a, seg->b, seg->r);+ if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);+}++void+cpSegmentShapeSetRadius(cpShape *shape, cpFloat radius)+{+ cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");+ cpSegmentShape *seg = (cpSegmentShape *)shape;+ + seg->r = radius;++ cpFloat mass = shape->massInfo.m;+ shape->massInfo = cpSegmentShapeMassInfo(shape->massInfo.m, seg->a, seg->b, seg->r);+ if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);+}
+ Chipmunk2D-7.0.2/src/cpSimpleMotor.c view
@@ -0,0 +1,123 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++static void+preStep(cpSimpleMotor *joint, cpFloat dt)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + // calculate moment of inertia coefficient.+ joint->iSum = 1.0f/(a->i_inv + b->i_inv);+}++static void+applyCachedImpulse(cpSimpleMotor *joint, cpFloat dt_coef)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + cpFloat j = joint->jAcc*dt_coef;+ a->w -= j*a->i_inv;+ b->w += j*b->i_inv;+}++static void+applyImpulse(cpSimpleMotor *joint, cpFloat dt)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + // compute relative rotational velocity+ cpFloat wr = b->w - a->w + joint->rate;+ + cpFloat jMax = joint->constraint.maxForce*dt;+ + // compute normal impulse + cpFloat j = -wr*joint->iSum;+ cpFloat jOld = joint->jAcc;+ joint->jAcc = cpfclamp(jOld + j, -jMax, jMax);+ j = joint->jAcc - jOld;+ + // apply impulse+ a->w -= j*a->i_inv;+ b->w += j*b->i_inv;+}++static cpFloat+getImpulse(cpSimpleMotor *joint)+{+ return cpfabs(joint->jAcc);+}++static const cpConstraintClass klass = {+ (cpConstraintPreStepImpl)preStep,+ (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,+ (cpConstraintApplyImpulseImpl)applyImpulse,+ (cpConstraintGetImpulseImpl)getImpulse,+};++cpSimpleMotor *+cpSimpleMotorAlloc(void)+{+ return (cpSimpleMotor *)cpcalloc(1, sizeof(cpSimpleMotor));+}++cpSimpleMotor *+cpSimpleMotorInit(cpSimpleMotor *joint, cpBody *a, cpBody *b, cpFloat rate)+{+ cpConstraintInit((cpConstraint *)joint, &klass, a, b);+ + joint->rate = rate;+ + joint->jAcc = 0.0f;+ + return joint;+}++cpConstraint *+cpSimpleMotorNew(cpBody *a, cpBody *b, cpFloat rate)+{+ return (cpConstraint *)cpSimpleMotorInit(cpSimpleMotorAlloc(), a, b, rate);+}++cpBool+cpConstraintIsSimpleMotor(const cpConstraint *constraint)+{+ return (constraint->klass == &klass);+}++cpFloat+cpSimpleMotorGetRate(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsSimpleMotor(constraint), "Constraint is not a pin joint.");+ return ((cpSimpleMotor *)constraint)->rate;+}++void+cpSimpleMotorSetRate(cpConstraint *constraint, cpFloat rate)+{+ cpAssertHard(cpConstraintIsSimpleMotor(constraint), "Constraint is not a pin joint.");+ cpConstraintActivateBodies(constraint);+ ((cpSimpleMotor *)constraint)->rate = rate;+}
+ Chipmunk2D-7.0.2/src/cpSlideJoint.c view
@@ -0,0 +1,195 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++static void+preStep(cpSlideJoint *joint, cpFloat dt)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + joint->r1 = cpTransformVect(a->transform, cpvsub(joint->anchorA, a->cog));+ joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog));+ + cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1));+ cpFloat dist = cpvlength(delta);+ cpFloat pdist = 0.0f;+ if(dist > joint->max) {+ pdist = dist - joint->max;+ joint->n = cpvnormalize(delta);+ } else if(dist < joint->min) {+ pdist = joint->min - dist;+ joint->n = cpvneg(cpvnormalize(delta));+ } else {+ joint->n = cpvzero;+ joint->jnAcc = 0.0f;+ }+ + // calculate mass normal+ joint->nMass = 1.0f/k_scalar(a, b, joint->r1, joint->r2, joint->n);+ + // calculate bias velocity+ cpFloat maxBias = joint->constraint.maxBias;+ joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*pdist/dt, -maxBias, maxBias);+}++static void+applyCachedImpulse(cpSlideJoint *joint, cpFloat dt_coef)+{+ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + cpVect j = cpvmult(joint->n, joint->jnAcc*dt_coef);+ apply_impulses(a, b, joint->r1, joint->r2, j);+}++static void+applyImpulse(cpSlideJoint *joint, cpFloat dt)+{+ if(cpveql(joint->n, cpvzero)) return; // early exit++ cpBody *a = joint->constraint.a;+ cpBody *b = joint->constraint.b;+ + cpVect n = joint->n;+ cpVect r1 = joint->r1;+ cpVect r2 = joint->r2;+ + // compute relative velocity+ cpVect vr = relative_velocity(a, b, r1, r2);+ cpFloat vrn = cpvdot(vr, n);+ + // compute normal impulse+ cpFloat jn = (joint->bias - vrn)*joint->nMass;+ cpFloat jnOld = joint->jnAcc;+ joint->jnAcc = cpfclamp(jnOld + jn, -joint->constraint.maxForce*dt, 0.0f);+ jn = joint->jnAcc - jnOld;+ + // apply impulse+ apply_impulses(a, b, joint->r1, joint->r2, cpvmult(n, jn));+}++static cpFloat+getImpulse(cpConstraint *joint)+{+ return cpfabs(((cpSlideJoint *)joint)->jnAcc);+}++static const cpConstraintClass klass = {+ (cpConstraintPreStepImpl)preStep,+ (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,+ (cpConstraintApplyImpulseImpl)applyImpulse,+ (cpConstraintGetImpulseImpl)getImpulse,+};++cpSlideJoint *+cpSlideJointAlloc(void)+{+ return (cpSlideJoint *)cpcalloc(1, sizeof(cpSlideJoint));+}++cpSlideJoint *+cpSlideJointInit(cpSlideJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max)+{+ cpConstraintInit((cpConstraint *)joint, &klass, a, b);+ + joint->anchorA = anchorA;+ joint->anchorB = anchorB;+ joint->min = min;+ joint->max = max;+ + joint->jnAcc = 0.0f;+ + return joint;+}++cpConstraint *+cpSlideJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max)+{+ return (cpConstraint *)cpSlideJointInit(cpSlideJointAlloc(), a, b, anchorA, anchorB, min, max);+}++cpBool+cpConstraintIsSlideJoint(const cpConstraint *constraint)+{+ return (constraint->klass == &klass);+}++cpVect+cpSlideJointGetAnchorA(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");+ return ((cpSlideJoint *)constraint)->anchorA;+}++void+cpSlideJointSetAnchorA(cpConstraint *constraint, cpVect anchorA)+{+ cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");+ cpConstraintActivateBodies(constraint);+ ((cpSlideJoint *)constraint)->anchorA = anchorA;+}++cpVect+cpSlideJointGetAnchorB(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");+ return ((cpSlideJoint *)constraint)->anchorB;+}++void+cpSlideJointSetAnchorB(cpConstraint *constraint, cpVect anchorB)+{+ cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");+ cpConstraintActivateBodies(constraint);+ ((cpSlideJoint *)constraint)->anchorB = anchorB;+}++cpFloat+cpSlideJointGetMin(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");+ return ((cpSlideJoint *)constraint)->min;+}++void+cpSlideJointSetMin(cpConstraint *constraint, cpFloat min)+{+ cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");+ cpConstraintActivateBodies(constraint);+ ((cpSlideJoint *)constraint)->min = min;+}++cpFloat+cpSlideJointGetMax(const cpConstraint *constraint)+{+ cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");+ return ((cpSlideJoint *)constraint)->max;+}++void+cpSlideJointSetMax(cpConstraint *constraint, cpFloat max)+{+ cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");+ cpConstraintActivateBodies(constraint);+ ((cpSlideJoint *)constraint)->max = max;+}
+ Chipmunk2D-7.0.2/src/cpSpace.c view
@@ -0,0 +1,700 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include <stdio.h>+#include <string.h>++#include "chipmunk/chipmunk_private.h"++//MARK: Contact Set Helpers++// Equal function for arbiterSet.+static cpBool+arbiterSetEql(cpShape **shapes, cpArbiter *arb)+{+ cpShape *a = shapes[0];+ cpShape *b = shapes[1];+ + return ((a == arb->a && b == arb->b) || (b == arb->a && a == arb->b));+}++//MARK: Collision Handler Set HelperFunctions++// Equals function for collisionHandlers.+static cpBool+handlerSetEql(cpCollisionHandler *check, cpCollisionHandler *pair)+{+ return (+ (check->typeA == pair->typeA && check->typeB == pair->typeB) ||+ (check->typeB == pair->typeA && check->typeA == pair->typeB)+ );+}++// Transformation function for collisionHandlers.+static void *+handlerSetTrans(cpCollisionHandler *handler, void *unused)+{+ cpCollisionHandler *copy = (cpCollisionHandler *)cpcalloc(1, sizeof(cpCollisionHandler));+ memcpy(copy, handler, sizeof(cpCollisionHandler));+ + return copy;+}++//MARK: Misc Helper Funcs++// Default collision functions.++static cpBool+DefaultBegin(cpArbiter *arb, cpSpace *space, void *data){+ cpBool retA = cpArbiterCallWildcardBeginA(arb, space);+ cpBool retB = cpArbiterCallWildcardBeginB(arb, space);+ return retA && retB;+}++static cpBool+DefaultPreSolve(cpArbiter *arb, cpSpace *space, void *data){+ cpBool retA = cpArbiterCallWildcardPreSolveA(arb, space);+ cpBool retB = cpArbiterCallWildcardPreSolveB(arb, space);+ return retA && retB;+}++static void+DefaultPostSolve(cpArbiter *arb, cpSpace *space, void *data){+ cpArbiterCallWildcardPostSolveA(arb, space);+ cpArbiterCallWildcardPostSolveB(arb, space);+}++static void+DefaultSeparate(cpArbiter *arb, cpSpace *space, void *data){+ cpArbiterCallWildcardSeparateA(arb, space);+ cpArbiterCallWildcardSeparateB(arb, space);+}++// Use the wildcard identifier since the default handler should never match any type pair.+static cpCollisionHandler cpCollisionHandlerDefault = {+ CP_WILDCARD_COLLISION_TYPE, CP_WILDCARD_COLLISION_TYPE,+ DefaultBegin, DefaultPreSolve, DefaultPostSolve, DefaultSeparate, NULL+};++static cpBool AlwaysCollide(cpArbiter *arb, cpSpace *space, void *data){return cpTrue;}+static void DoNothing(cpArbiter *arb, cpSpace *space, void *data){}++cpCollisionHandler cpCollisionHandlerDoNothing = {+ CP_WILDCARD_COLLISION_TYPE, CP_WILDCARD_COLLISION_TYPE,+ AlwaysCollide, AlwaysCollide, DoNothing, DoNothing, NULL+};++// function to get the estimated velocity of a shape for the cpBBTree.+static cpVect ShapeVelocityFunc(cpShape *shape){return shape->body->v;}++// Used for disposing of collision handlers.+static void FreeWrap(void *ptr, void *unused){cpfree(ptr);}++//MARK: Memory Management Functions++cpSpace *+cpSpaceAlloc(void)+{+ return (cpSpace *)cpcalloc(1, sizeof(cpSpace));+}++cpSpace*+cpSpaceInit(cpSpace *space)+{+#ifndef NDEBUG+ static cpBool done = cpFalse;+ if(!done){+ printf("Initializing cpSpace - Chipmunk v%s (Debug Enabled)\n", cpVersionString);+ printf("Compile with -DNDEBUG defined to disable debug mode and runtime assertion checks\n");+ done = cpTrue;+ }+#endif++ space->iterations = 10;+ + space->gravity = cpvzero;+ space->damping = 1.0f;+ + space->collisionSlop = 0.1f;+ space->collisionBias = cpfpow(1.0f - 0.1f, 60.0f);+ space->collisionPersistence = 3;+ + space->locked = 0;+ space->stamp = 0;+ + space->shapeIDCounter = 0;+ space->staticShapes = cpBBTreeNew((cpSpatialIndexBBFunc)cpShapeGetBB, NULL);+ space->dynamicShapes = cpBBTreeNew((cpSpatialIndexBBFunc)cpShapeGetBB, space->staticShapes);+ cpBBTreeSetVelocityFunc(space->dynamicShapes, (cpBBTreeVelocityFunc)ShapeVelocityFunc);+ + space->allocatedBuffers = cpArrayNew(0);+ + space->dynamicBodies = cpArrayNew(0);+ space->staticBodies = cpArrayNew(0);+ space->sleepingComponents = cpArrayNew(0);+ space->rousedBodies = cpArrayNew(0);+ + space->sleepTimeThreshold = INFINITY;+ space->idleSpeedThreshold = 0.0f;+ + space->arbiters = cpArrayNew(0);+ space->pooledArbiters = cpArrayNew(0);+ + space->contactBuffersHead = NULL;+ space->cachedArbiters = cpHashSetNew(0, (cpHashSetEqlFunc)arbiterSetEql);+ + space->constraints = cpArrayNew(0);+ + space->usesWildcards = cpFalse;+ memcpy(&space->defaultHandler, &cpCollisionHandlerDoNothing, sizeof(cpCollisionHandler));+ space->collisionHandlers = cpHashSetNew(0, (cpHashSetEqlFunc)handlerSetEql);+ + space->postStepCallbacks = cpArrayNew(0);+ space->skipPostStep = cpFalse;+ + cpBody *staticBody = cpBodyInit(&space->_staticBody, 0.0f, 0.0f);+ cpBodySetType(staticBody, CP_BODY_TYPE_STATIC);+ cpSpaceSetStaticBody(space, staticBody);+ + return space;+}++cpSpace*+cpSpaceNew(void)+{+ return cpSpaceInit(cpSpaceAlloc());+}++static void cpBodyActivateWrap(cpBody *body, void *unused){cpBodyActivate(body);}++void+cpSpaceDestroy(cpSpace *space)+{+ cpSpaceEachBody(space, (cpSpaceBodyIteratorFunc)cpBodyActivateWrap, NULL);+ + cpSpatialIndexFree(space->staticShapes);+ cpSpatialIndexFree(space->dynamicShapes);+ + cpArrayFree(space->dynamicBodies);+ cpArrayFree(space->staticBodies);+ cpArrayFree(space->sleepingComponents);+ cpArrayFree(space->rousedBodies);+ + cpArrayFree(space->constraints);+ + cpHashSetFree(space->cachedArbiters);+ + cpArrayFree(space->arbiters);+ cpArrayFree(space->pooledArbiters);+ + if(space->allocatedBuffers){+ cpArrayFreeEach(space->allocatedBuffers, cpfree);+ cpArrayFree(space->allocatedBuffers);+ }+ + if(space->postStepCallbacks){+ cpArrayFreeEach(space->postStepCallbacks, cpfree);+ cpArrayFree(space->postStepCallbacks);+ }+ + if(space->collisionHandlers) cpHashSetEach(space->collisionHandlers, FreeWrap, NULL);+ cpHashSetFree(space->collisionHandlers);+}++void+cpSpaceFree(cpSpace *space)+{+ if(space){+ cpSpaceDestroy(space);+ cpfree(space);+ }+}+++//MARK: Basic properties:++int+cpSpaceGetIterations(const cpSpace *space)+{+ return space->iterations;+}++void+cpSpaceSetIterations(cpSpace *space, int iterations)+{+ cpAssertHard(iterations > 0, "Iterations must be positive and non-zero.");+ space->iterations = iterations;+}++cpVect+cpSpaceGetGravity(const cpSpace *space)+{+ return space->gravity;+}++void+cpSpaceSetGravity(cpSpace *space, cpVect gravity)+{+ space->gravity = gravity;+ + // Wake up all of the bodies since the gravity changed.+ cpArray *components = space->sleepingComponents;+ for(int i=0; i<components->num; i++){+ cpBodyActivate((cpBody *)components->arr[i]);+ }+}++cpFloat+cpSpaceGetDamping(const cpSpace *space)+{+ return space->damping;+}++void+cpSpaceSetDamping(cpSpace *space, cpFloat damping)+{+ cpAssertHard(damping >= 0.0, "Damping must be positive.");+ space->damping = damping;+}++cpFloat+cpSpaceGetIdleSpeedThreshold(const cpSpace *space)+{+ return space->idleSpeedThreshold;+}++void+cpSpaceSetIdleSpeedThreshold(cpSpace *space, cpFloat idleSpeedThreshold)+{+ space->idleSpeedThreshold = idleSpeedThreshold;+}++cpFloat+cpSpaceGetSleepTimeThreshold(const cpSpace *space)+{+ return space->sleepTimeThreshold;+}++void+cpSpaceSetSleepTimeThreshold(cpSpace *space, cpFloat sleepTimeThreshold)+{+ space->sleepTimeThreshold = sleepTimeThreshold;+}++cpFloat+cpSpaceGetCollisionSlop(const cpSpace *space)+{+ return space->collisionSlop;+}++void+cpSpaceSetCollisionSlop(cpSpace *space, cpFloat collisionSlop)+{+ space->collisionSlop = collisionSlop;+}++cpFloat+cpSpaceGetCollisionBias(const cpSpace *space)+{+ return space->collisionBias;+}++void+cpSpaceSetCollisionBias(cpSpace *space, cpFloat collisionBias)+{+ space->collisionBias = collisionBias;+}++cpTimestamp+cpSpaceGetCollisionPersistence(const cpSpace *space)+{+ return space->collisionPersistence;+}++void+cpSpaceSetCollisionPersistence(cpSpace *space, cpTimestamp collisionPersistence)+{+ space->collisionPersistence = collisionPersistence;+}++cpDataPointer+cpSpaceGetUserData(const cpSpace *space)+{+ return space->userData;+}++void+cpSpaceSetUserData(cpSpace *space, cpDataPointer userData)+{+ space->userData = userData;+}++cpBody *+cpSpaceGetStaticBody(const cpSpace *space)+{+ return space->staticBody;+}++cpFloat+cpSpaceGetCurrentTimeStep(const cpSpace *space)+{+ return space->curr_dt;+}++void+cpSpaceSetStaticBody(cpSpace *space, cpBody *body)+{+ if(space->staticBody != NULL){+ cpAssertHard(space->staticBody->shapeList == NULL, "Internal Error: Changing the designated static body while the old one still had shapes attached.");+ space->staticBody->space = NULL;+ }+ + space->staticBody = body;+ body->space = space;+}++cpBool+cpSpaceIsLocked(cpSpace *space)+{+ return (space->locked > 0);+}++//MARK: Collision Handler Function Management++static void+cpSpaceUseWildcardDefaultHandler(cpSpace *space)+{+ // Spaces default to using the slightly faster "do nothing" default handler until wildcards are potentially needed.+ if(!space->usesWildcards){+ space->usesWildcards = cpTrue;+ memcpy(&space->defaultHandler, &cpCollisionHandlerDefault, sizeof(cpCollisionHandler));+ }+}++cpCollisionHandler *cpSpaceAddDefaultCollisionHandler(cpSpace *space)+{+ cpSpaceUseWildcardDefaultHandler(space);+ return &space->defaultHandler;+}++cpCollisionHandler *cpSpaceAddCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b)+{+ cpHashValue hash = CP_HASH_PAIR(a, b);+ cpCollisionHandler handler = {a, b, DefaultBegin, DefaultPreSolve, DefaultPostSolve, DefaultSeparate, NULL};+ return (cpCollisionHandler*)cpHashSetInsert(space->collisionHandlers, hash, &handler, (cpHashSetTransFunc)handlerSetTrans, NULL);+}++cpCollisionHandler *+cpSpaceAddWildcardHandler(cpSpace *space, cpCollisionType type)+{+ cpSpaceUseWildcardDefaultHandler(space);+ + cpHashValue hash = CP_HASH_PAIR(type, CP_WILDCARD_COLLISION_TYPE);+ cpCollisionHandler handler = {type, CP_WILDCARD_COLLISION_TYPE, AlwaysCollide, AlwaysCollide, DoNothing, DoNothing, NULL};+ return (cpCollisionHandler*)cpHashSetInsert(space->collisionHandlers, hash, &handler, (cpHashSetTransFunc)handlerSetTrans, NULL);+}+++//MARK: Body, Shape, and Joint Management+cpShape *+cpSpaceAddShape(cpSpace *space, cpShape *shape)+{+ cpBody *body = shape->body;+ + cpAssertHard(shape->space != space, "You have already added this shape to this space. You must not add it a second time.");+ cpAssertHard(!shape->space, "You have already added this shape to another space. You cannot add it to a second.");+// cpAssertHard(body->space == space, "The shape's body must be added to the space before the shape.");+ cpAssertSpaceUnlocked(space);+ + cpBool isStatic = (cpBodyGetType(body) == CP_BODY_TYPE_STATIC);+ if(!isStatic) cpBodyActivate(body);+ cpBodyAddShape(body, shape);+ + shape->hashid = space->shapeIDCounter++;+ cpShapeUpdate(shape, body->transform);+ cpSpatialIndexInsert(isStatic ? space->staticShapes : space->dynamicShapes, shape, shape->hashid);+ shape->space = space;+ + return shape;+}++cpBody *+cpSpaceAddBody(cpSpace *space, cpBody *body)+{+ cpAssertHard(body->space != space, "You have already added this body to this space. You must not add it a second time.");+ cpAssertHard(!body->space, "You have already added this body to another space. You cannot add it to a second.");+ cpAssertSpaceUnlocked(space);+ + cpArrayPush(cpSpaceArrayForBodyType(space, cpBodyGetType(body)), body);+ body->space = space;+ + return body;+}++cpConstraint *+cpSpaceAddConstraint(cpSpace *space, cpConstraint *constraint)+{+ cpAssertHard(constraint->space != space, "You have already added this constraint to this space. You must not add it a second time.");+ cpAssertHard(!constraint->space, "You have already added this constraint to another space. You cannot add it to a second.");+ cpAssertSpaceUnlocked(space);+ + cpBody *a = constraint->a, *b = constraint->b;+ cpAssertHard(a != NULL && b != NULL, "Constraint is attached to a NULL body.");+// cpAssertHard(a->space == space && b->space == space, "The constraint's bodies must be added to the space before the constraint.");+ + cpBodyActivate(a);+ cpBodyActivate(b);+ cpArrayPush(space->constraints, constraint);+ + // Push onto the heads of the bodies' constraint lists+ constraint->next_a = a->constraintList; a->constraintList = constraint;+ constraint->next_b = b->constraintList; b->constraintList = constraint;+ constraint->space = space;+ + return constraint;+}++struct arbiterFilterContext {+ cpSpace *space;+ cpBody *body;+ cpShape *shape;+};++static cpBool+cachedArbitersFilter(cpArbiter *arb, struct arbiterFilterContext *context)+{+ cpShape *shape = context->shape;+ cpBody *body = context->body;+ + + // Match on the filter shape, or if it's NULL the filter body+ if(+ (body == arb->body_a && (shape == arb->a || shape == NULL)) ||+ (body == arb->body_b && (shape == arb->b || shape == NULL))+ ){+ // Call separate when removing shapes.+ if(shape && arb->state != CP_ARBITER_STATE_CACHED){+ // Invalidate the arbiter since one of the shapes was removed.+ arb->state = CP_ARBITER_STATE_INVALIDATED;+ + cpCollisionHandler *handler = arb->handler;+ handler->separateFunc(arb, context->space, handler->userData);+ }+ + cpArbiterUnthread(arb);+ cpArrayDeleteObj(context->space->arbiters, arb);+ cpArrayPush(context->space->pooledArbiters, arb);+ + return cpFalse;+ }+ + return cpTrue;+}++void+cpSpaceFilterArbiters(cpSpace *space, cpBody *body, cpShape *filter)+{+ cpSpaceLock(space); {+ struct arbiterFilterContext context = {space, body, filter};+ cpHashSetFilter(space->cachedArbiters, (cpHashSetFilterFunc)cachedArbitersFilter, &context);+ } cpSpaceUnlock(space, cpTrue);+}++void+cpSpaceRemoveShape(cpSpace *space, cpShape *shape)+{+ cpBody *body = shape->body;+ cpAssertHard(cpSpaceContainsShape(space, shape), "Cannot remove a shape that was not added to the space. (Removed twice maybe?)");+ cpAssertSpaceUnlocked(space);+ + cpBool isStatic = (cpBodyGetType(body) == CP_BODY_TYPE_STATIC);+ if(isStatic){+ cpBodyActivateStatic(body, shape);+ } else {+ cpBodyActivate(body);+ }++ cpBodyRemoveShape(body, shape);+ cpSpaceFilterArbiters(space, body, shape);+ cpSpatialIndexRemove(isStatic ? space->staticShapes : space->dynamicShapes, shape, shape->hashid);+ shape->space = NULL;+ shape->hashid = 0;+}++void+cpSpaceRemoveBody(cpSpace *space, cpBody *body)+{+ cpAssertHard(body != cpSpaceGetStaticBody(space), "Cannot remove the designated static body for the space.");+ cpAssertHard(cpSpaceContainsBody(space, body), "Cannot remove a body that was not added to the space. (Removed twice maybe?)");+// cpAssertHard(body->shapeList == NULL, "Cannot remove a body from the space before removing the bodies attached to it.");+// cpAssertHard(body->constraintList == NULL, "Cannot remove a body from the space before removing the constraints attached to it.");+ cpAssertSpaceUnlocked(space);+ + cpBodyActivate(body);+// cpSpaceFilterArbiters(space, body, NULL);+ cpArrayDeleteObj(cpSpaceArrayForBodyType(space, cpBodyGetType(body)), body);+ body->space = NULL;+}++void+cpSpaceRemoveConstraint(cpSpace *space, cpConstraint *constraint)+{+ cpAssertHard(cpSpaceContainsConstraint(space, constraint), "Cannot remove a constraint that was not added to the space. (Removed twice maybe?)");+ cpAssertSpaceUnlocked(space);+ + cpBodyActivate(constraint->a);+ cpBodyActivate(constraint->b);+ cpArrayDeleteObj(space->constraints, constraint);+ + cpBodyRemoveConstraint(constraint->a, constraint);+ cpBodyRemoveConstraint(constraint->b, constraint);+ constraint->space = NULL;+}++cpBool cpSpaceContainsShape(cpSpace *space, cpShape *shape)+{+ return (shape->space == space);+}++cpBool cpSpaceContainsBody(cpSpace *space, cpBody *body)+{+ return (body->space == space);+}++cpBool cpSpaceContainsConstraint(cpSpace *space, cpConstraint *constraint)+{+ return (constraint->space == space);+}++//MARK: Iteration++void+cpSpaceEachBody(cpSpace *space, cpSpaceBodyIteratorFunc func, void *data)+{+ cpSpaceLock(space); {+ cpArray *bodies = space->dynamicBodies;+ for(int i=0; i<bodies->num; i++){+ func((cpBody *)bodies->arr[i], data);+ }+ + cpArray *otherBodies = space->staticBodies;+ for(int i=0; i<otherBodies->num; i++){+ func((cpBody *)otherBodies->arr[i], data);+ }+ + cpArray *components = space->sleepingComponents;+ for(int i=0; i<components->num; i++){+ cpBody *root = (cpBody *)components->arr[i];+ + cpBody *body = root;+ while(body){+ cpBody *next = body->sleeping.next;+ func(body, data);+ body = next;+ }+ }+ } cpSpaceUnlock(space, cpTrue);+}++typedef struct spaceShapeContext {+ cpSpaceShapeIteratorFunc func;+ void *data;+} spaceShapeContext;++static void+spaceEachShapeIterator(cpShape *shape, spaceShapeContext *context)+{+ context->func(shape, context->data);+}++void+cpSpaceEachShape(cpSpace *space, cpSpaceShapeIteratorFunc func, void *data)+{+ cpSpaceLock(space); {+ spaceShapeContext context = {func, data};+ cpSpatialIndexEach(space->dynamicShapes, (cpSpatialIndexIteratorFunc)spaceEachShapeIterator, &context);+ cpSpatialIndexEach(space->staticShapes, (cpSpatialIndexIteratorFunc)spaceEachShapeIterator, &context);+ } cpSpaceUnlock(space, cpTrue);+}++void+cpSpaceEachConstraint(cpSpace *space, cpSpaceConstraintIteratorFunc func, void *data)+{+ cpSpaceLock(space); {+ cpArray *constraints = space->constraints;+ + for(int i=0; i<constraints->num; i++){+ func((cpConstraint *)constraints->arr[i], data);+ }+ } cpSpaceUnlock(space, cpTrue);+}++//MARK: Spatial Index Management++void +cpSpaceReindexStatic(cpSpace *space)+{+ cpAssertHard(!space->locked, "You cannot manually reindex objects while the space is locked. Wait until the current query or step is complete.");+ + cpSpatialIndexEach(space->staticShapes, (cpSpatialIndexIteratorFunc)&cpShapeUpdateFunc, NULL);+ cpSpatialIndexReindex(space->staticShapes);+}++void+cpSpaceReindexShape(cpSpace *space, cpShape *shape)+{+ cpAssertHard(!space->locked, "You cannot manually reindex objects while the space is locked. Wait until the current query or step is complete.");+ + cpShapeCacheBB(shape);+ + // attempt to rehash the shape in both hashes+ cpSpatialIndexReindexObject(space->dynamicShapes, shape, shape->hashid);+ cpSpatialIndexReindexObject(space->staticShapes, shape, shape->hashid);+}++void+cpSpaceReindexShapesForBody(cpSpace *space, cpBody *body)+{+ CP_BODY_FOREACH_SHAPE(body, shape) cpSpaceReindexShape(space, shape);+}+++static void+copyShapes(cpShape *shape, cpSpatialIndex *index)+{+ cpSpatialIndexInsert(index, shape, shape->hashid);+}++void+cpSpaceUseSpatialHash(cpSpace *space, cpFloat dim, int count)+{+ cpSpatialIndex *staticShapes = cpSpaceHashNew(dim, count, (cpSpatialIndexBBFunc)cpShapeGetBB, NULL);+ cpSpatialIndex *dynamicShapes = cpSpaceHashNew(dim, count, (cpSpatialIndexBBFunc)cpShapeGetBB, staticShapes);+ + cpSpatialIndexEach(space->staticShapes, (cpSpatialIndexIteratorFunc)copyShapes, staticShapes);+ cpSpatialIndexEach(space->dynamicShapes, (cpSpatialIndexIteratorFunc)copyShapes, dynamicShapes);+ + cpSpatialIndexFree(space->staticShapes);+ cpSpatialIndexFree(space->dynamicShapes);+ + space->staticShapes = staticShapes;+ space->dynamicShapes = dynamicShapes;+}
+ Chipmunk2D-7.0.2/src/cpSpaceComponent.c view
@@ -0,0 +1,349 @@+/* Copyright (c) 2007 Scott Lembcke+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */+ +#include <string.h>++#include "chipmunk/chipmunk_private.h"++//MARK: Sleeping Functions++void+cpSpaceActivateBody(cpSpace *space, cpBody *body)+{+ cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "Internal error: Attempting to activate a non-dynamic body.");+ + if(space->locked){+ // cpSpaceActivateBody() is called again once the space is unlocked+ if(!cpArrayContains(space->rousedBodies, body)) cpArrayPush(space->rousedBodies, body);+ } else {+ cpAssertSoft(body->sleeping.root == NULL && body->sleeping.next == NULL, "Internal error: Activating body non-NULL node pointers.");+ cpArrayPush(space->dynamicBodies, body);++ CP_BODY_FOREACH_SHAPE(body, shape){+ cpSpatialIndexRemove(space->staticShapes, shape, shape->hashid);+ cpSpatialIndexInsert(space->dynamicShapes, shape, shape->hashid);+ }+ + CP_BODY_FOREACH_ARBITER(body, arb){+ cpBody *bodyA = arb->body_a;+ + // Arbiters are shared between two bodies that are always woken up together.+ // You only want to restore the arbiter once, so bodyA is arbitrarily chosen to own the arbiter.+ // The edge case is when static bodies are involved as the static bodies never actually sleep.+ // If the static body is bodyB then all is good. If the static body is bodyA, that can easily be checked.+ if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC){+ int numContacts = arb->count;+ struct cpContact *contacts = arb->contacts;+ + // Restore contact values back to the space's contact buffer memory+ arb->contacts = cpContactBufferGetArray(space);+ memcpy(arb->contacts, contacts, numContacts*sizeof(struct cpContact));+ cpSpacePushContacts(space, numContacts);+ + // Reinsert the arbiter into the arbiter cache+ const cpShape *a = arb->a, *b = arb->b;+ const cpShape *shape_pair[] = {a, b};+ cpHashValue arbHashID = CP_HASH_PAIR((cpHashValue)a, (cpHashValue)b);+ cpHashSetInsert(space->cachedArbiters, arbHashID, shape_pair, NULL, arb);+ + // Update the arbiter's state+ arb->stamp = space->stamp;+ cpArrayPush(space->arbiters, arb);+ + cpfree(contacts);+ }+ }+ + CP_BODY_FOREACH_CONSTRAINT(body, constraint){+ cpBody *bodyA = constraint->a;+ if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC) cpArrayPush(space->constraints, constraint);+ }+ }+}++static void+cpSpaceDeactivateBody(cpSpace *space, cpBody *body)+{+ cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "Internal error: Attempting to deactivate a non-dynamic body.");+ + cpArrayDeleteObj(space->dynamicBodies, body);+ + CP_BODY_FOREACH_SHAPE(body, shape){+ cpSpatialIndexRemove(space->dynamicShapes, shape, shape->hashid);+ cpSpatialIndexInsert(space->staticShapes, shape, shape->hashid);+ }+ + CP_BODY_FOREACH_ARBITER(body, arb){+ cpBody *bodyA = arb->body_a;+ if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC){+ cpSpaceUncacheArbiter(space, arb);+ + // Save contact values to a new block of memory so they won't time out+ size_t bytes = arb->count*sizeof(struct cpContact);+ struct cpContact *contacts = (struct cpContact *)cpcalloc(1, bytes);+ memcpy(contacts, arb->contacts, bytes);+ arb->contacts = contacts;+ }+ }+ + CP_BODY_FOREACH_CONSTRAINT(body, constraint){+ cpBody *bodyA = constraint->a;+ if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC) cpArrayDeleteObj(space->constraints, constraint);+ }+}++static inline cpBody *+ComponentRoot(cpBody *body)+{+ return (body ? body->sleeping.root : NULL);+}++void+cpBodyActivate(cpBody *body)+{+ if(body != NULL && cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC){+ body->sleeping.idleTime = 0.0f;+ + cpBody *root = ComponentRoot(body);+ if(root && cpBodyIsSleeping(root)){+ // TODO should cpBodyIsSleeping(root) be an assertion?+ cpAssertSoft(cpBodyGetType(root) == CP_BODY_TYPE_DYNAMIC, "Internal Error: Non-dynamic body component root detected.");+ + cpSpace *space = root->space;+ cpBody *body = root;+ while(body){+ cpBody *next = body->sleeping.next;+ + body->sleeping.idleTime = 0.0f;+ body->sleeping.root = NULL;+ body->sleeping.next = NULL;+ cpSpaceActivateBody(space, body);+ + body = next;+ }+ + cpArrayDeleteObj(space->sleepingComponents, root);+ }+ + CP_BODY_FOREACH_ARBITER(body, arb){+ // Reset the idle timer of things the body is touching as well.+ // That way things don't get left hanging in the air.+ cpBody *other = (arb->body_a == body ? arb->body_b : arb->body_a);+ if(cpBodyGetType(other) != CP_BODY_TYPE_STATIC) other->sleeping.idleTime = 0.0f;+ }+ }+}++void+cpBodyActivateStatic(cpBody *body, cpShape *filter)+{+ cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_STATIC, "cpBodyActivateStatic() called on a non-static body.");+ + CP_BODY_FOREACH_ARBITER(body, arb){+ if(!filter || filter == arb->a || filter == arb->b){+ cpBodyActivate(arb->body_a == body ? arb->body_b : arb->body_a);+ }+ }+ + // TODO: should also activate joints?+}++static inline void+cpBodyPushArbiter(cpBody *body, cpArbiter *arb)+{+ cpAssertSoft(cpArbiterThreadForBody(arb, body)->next == NULL, "Internal Error: Dangling contact graph pointers detected. (A)");+ cpAssertSoft(cpArbiterThreadForBody(arb, body)->prev == NULL, "Internal Error: Dangling contact graph pointers detected. (B)");+ + cpArbiter *next = body->arbiterList;+ cpAssertSoft(next == NULL || cpArbiterThreadForBody(next, body)->prev == NULL, "Internal Error: Dangling contact graph pointers detected. (C)");+ cpArbiterThreadForBody(arb, body)->next = next;+ + if(next) cpArbiterThreadForBody(next, body)->prev = arb;+ body->arbiterList = arb;+}++static inline void+ComponentAdd(cpBody *root, cpBody *body){+ body->sleeping.root = root;++ if(body != root){+ body->sleeping.next = root->sleeping.next;+ root->sleeping.next = body;+ }+}++static inline void+FloodFillComponent(cpBody *root, cpBody *body)+{+ // Kinematic bodies cannot be put to sleep and prevent bodies they are touching from sleeping.+ // Static bodies are effectively sleeping all the time.+ if(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC){+ cpBody *other_root = ComponentRoot(body);+ if(other_root == NULL){+ ComponentAdd(root, body);+ CP_BODY_FOREACH_ARBITER(body, arb) FloodFillComponent(root, (body == arb->body_a ? arb->body_b : arb->body_a));+ CP_BODY_FOREACH_CONSTRAINT(body, constraint) FloodFillComponent(root, (body == constraint->a ? constraint->b : constraint->a));+ } else {+ cpAssertSoft(other_root == root, "Internal Error: Inconsistency dectected in the contact graph.");+ }+ }+}++static inline cpBool+ComponentActive(cpBody *root, cpFloat threshold)+{+ CP_BODY_FOREACH_COMPONENT(root, body){+ if(body->sleeping.idleTime < threshold) return cpTrue;+ }+ + return cpFalse;+}++void+cpSpaceProcessComponents(cpSpace *space, cpFloat dt)+{+ cpBool sleep = (space->sleepTimeThreshold != INFINITY);+ cpArray *bodies = space->dynamicBodies;+ +#ifndef NDEBUG+ for(int i=0; i<bodies->num; i++){+ cpBody *body = (cpBody*)bodies->arr[i];+ + cpAssertSoft(body->sleeping.next == NULL, "Internal Error: Dangling next pointer detected in contact graph.");+ cpAssertSoft(body->sleeping.root == NULL, "Internal Error: Dangling root pointer detected in contact graph.");+ }+#endif+ + // Calculate the kinetic energy of all the bodies.+ if(sleep){+ cpFloat dv = space->idleSpeedThreshold;+ cpFloat dvsq = (dv ? dv*dv : cpvlengthsq(space->gravity)*dt*dt);+ + // update idling and reset component nodes+ for(int i=0; i<bodies->num; i++){+ cpBody *body = (cpBody*)bodies->arr[i];+ + // TODO should make a separate array for kinematic bodies.+ if(cpBodyGetType(body) != CP_BODY_TYPE_DYNAMIC) continue;+ + // Need to deal with infinite mass objects+ cpFloat keThreshold = (dvsq ? body->m*dvsq : 0.0f);+ body->sleeping.idleTime = (cpBodyKineticEnergy(body) > keThreshold ? 0.0f : body->sleeping.idleTime + dt);+ }+ }+ + // Awaken any sleeping bodies found and then push arbiters to the bodies' lists.+ cpArray *arbiters = space->arbiters;+ for(int i=0, count=arbiters->num; i<count; i++){+ cpArbiter *arb = (cpArbiter*)arbiters->arr[i];+ cpBody *a = arb->body_a, *b = arb->body_b;+ + if(sleep){+ // TODO checking cpBodyIsSleepin() redundant?+ if(cpBodyGetType(b) == CP_BODY_TYPE_KINEMATIC || cpBodyIsSleeping(a)) cpBodyActivate(a);+ if(cpBodyGetType(a) == CP_BODY_TYPE_KINEMATIC || cpBodyIsSleeping(b)) cpBodyActivate(b);+ }+ + cpBodyPushArbiter(a, arb);+ cpBodyPushArbiter(b, arb);+ }+ + if(sleep){+ // Bodies should be held active if connected by a joint to a kinematic.+ cpArray *constraints = space->constraints;+ for(int i=0; i<constraints->num; i++){+ cpConstraint *constraint = (cpConstraint *)constraints->arr[i];+ cpBody *a = constraint->a, *b = constraint->b;+ + if(cpBodyGetType(b) == CP_BODY_TYPE_KINEMATIC) cpBodyActivate(a);+ if(cpBodyGetType(a) == CP_BODY_TYPE_KINEMATIC) cpBodyActivate(b);+ }+ + // Generate components and deactivate sleeping ones+ for(int i=0; i<bodies->num;){+ cpBody *body = (cpBody*)bodies->arr[i];+ + if(ComponentRoot(body) == NULL){+ // Body not in a component yet. Perform a DFS to flood fill mark + // the component in the contact graph using this body as the root.+ FloodFillComponent(body, body);+ + // Check if the component should be put to sleep.+ if(!ComponentActive(body, space->sleepTimeThreshold)){+ cpArrayPush(space->sleepingComponents, body);+ CP_BODY_FOREACH_COMPONENT(body, other) cpSpaceDeactivateBody(space, other);+ + // cpSpaceDeactivateBody() removed the current body from the list.+ // Skip incrementing the index counter.+ continue;+ }+ }+ + i++;+ + // Only sleeping bodies retain their component node pointers.+ body->sleeping.root = NULL;+ body->sleeping.next = NULL;+ }+ }+}++void+cpBodySleep(cpBody *body)+{+ cpBodySleepWithGroup(body, NULL);+}++void+cpBodySleepWithGroup(cpBody *body, cpBody *group){+ cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "Non-dynamic bodies cannot be put to sleep.");+ + cpSpace *space = body->space;+ cpAssertHard(!cpSpaceIsLocked(space), "Bodies cannot be put to sleep during a query or a call to cpSpaceStep(). Put these calls into a post-step callback.");+ cpAssertHard(cpSpaceGetSleepTimeThreshold(space) < INFINITY, "Sleeping is not enabled on the space. You cannot sleep a body without setting a sleep time threshold on the space.");+ cpAssertHard(group == NULL || cpBodyIsSleeping(group), "Cannot use a non-sleeping body as a group identifier.");+ + if(cpBodyIsSleeping(body)){+ cpAssertHard(ComponentRoot(body) == ComponentRoot(group), "The body is already sleeping and it's group cannot be reassigned.");+ return;+ }+ + CP_BODY_FOREACH_SHAPE(body, shape) cpShapeCacheBB(shape);+ cpSpaceDeactivateBody(space, body);+ + if(group){+ cpBody *root = ComponentRoot(group);+ + body->sleeping.root = root;+ body->sleeping.next = root->sleeping.next;+ body->sleeping.idleTime = 0.0f;+ + root->sleeping.next = body;+ } else {+ body->sleeping.root = body;+ body->sleeping.next = NULL;+ body->sleeping.idleTime = 0.0f;+ + cpArrayPush(space->sleepingComponents, body);+ }+ + cpArrayDeleteObj(space->dynamicBodies, body);+}
+ Chipmunk2D-7.0.2/src/cpSpaceDebug.c view
@@ -0,0 +1,189 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++#ifndef CP_SPACE_DISABLE_DEBUG_API++static void+cpSpaceDebugDrawShape(cpShape *shape, cpSpaceDebugDrawOptions *options)+{+ cpBody *body = shape->body;+ cpDataPointer data = options->data;+ + cpSpaceDebugColor outline_color = options->shapeOutlineColor;+ cpSpaceDebugColor fill_color = options->colorForShape(shape, data);+ + switch(shape->klass->type){+ case CP_CIRCLE_SHAPE: {+ cpCircleShape *circle = (cpCircleShape *)shape;+ options->drawCircle(circle->tc, body->a, circle->r, outline_color, fill_color, data);+ break;+ }+ case CP_SEGMENT_SHAPE: {+ cpSegmentShape *seg = (cpSegmentShape *)shape;+ options->drawFatSegment(seg->ta, seg->tb, seg->r, outline_color, fill_color, data);+ break;+ }+ case CP_POLY_SHAPE: {+ cpPolyShape *poly = (cpPolyShape *)shape;+ + int count = poly->count;+ struct cpSplittingPlane *planes = poly->planes;+ cpVect *verts = (cpVect *)alloca(count*sizeof(cpVect));+ + for(int i=0; i<count; i++) verts[i] = planes[i].v0;+ options->drawPolygon(count, verts, poly->r, outline_color, fill_color, data);+ break;+ }+ default: break;+ }+}++static const cpVect spring_verts[] = {+ {0.00f, 0.0f},+ {0.20f, 0.0f},+ {0.25f, 3.0f},+ {0.30f,-6.0f},+ {0.35f, 6.0f},+ {0.40f,-6.0f},+ {0.45f, 6.0f},+ {0.50f,-6.0f},+ {0.55f, 6.0f},+ {0.60f,-6.0f},+ {0.65f, 6.0f},+ {0.70f,-3.0f},+ {0.75f, 6.0f},+ {0.80f, 0.0f},+ {1.00f, 0.0f},+};+static const int spring_count = sizeof(spring_verts)/sizeof(cpVect);++static void+cpSpaceDebugDrawConstraint(cpConstraint *constraint, cpSpaceDebugDrawOptions *options)+{+ cpDataPointer data = options->data;+ cpSpaceDebugColor color = options->constraintColor;+ + cpBody *body_a = constraint->a;+ cpBody *body_b = constraint->b;++ if(cpConstraintIsPinJoint(constraint)){+ cpPinJoint *joint = (cpPinJoint *)constraint;+ + cpVect a = cpTransformPoint(body_a->transform, joint->anchorA);+ cpVect b = cpTransformPoint(body_b->transform, joint->anchorB);+ + options->drawDot(5, a, color, data);+ options->drawDot(5, b, color, data);+ options->drawSegment(a, b, color, data);+ } else if(cpConstraintIsSlideJoint(constraint)){+ cpSlideJoint *joint = (cpSlideJoint *)constraint;+ + cpVect a = cpTransformPoint(body_a->transform, joint->anchorA);+ cpVect b = cpTransformPoint(body_b->transform, joint->anchorB);+ + options->drawDot(5, a, color, data);+ options->drawDot(5, b, color, data);+ options->drawSegment(a, b, color, data);+ } else if(cpConstraintIsPivotJoint(constraint)){+ cpPivotJoint *joint = (cpPivotJoint *)constraint;+ + cpVect a = cpTransformPoint(body_a->transform, joint->anchorA);+ cpVect b = cpTransformPoint(body_b->transform, joint->anchorB);++ options->drawDot(5, a, color, data);+ options->drawDot(5, b, color, data);+ } else if(cpConstraintIsGrooveJoint(constraint)){+ cpGrooveJoint *joint = (cpGrooveJoint *)constraint;+ + cpVect a = cpTransformPoint(body_a->transform, joint->grv_a);+ cpVect b = cpTransformPoint(body_a->transform, joint->grv_b);+ cpVect c = cpTransformPoint(body_b->transform, joint->anchorB);+ + options->drawDot(5, c, color, data);+ options->drawSegment(a, b, color, data);+ } else if(cpConstraintIsDampedSpring(constraint)){+ cpDampedSpring *spring = (cpDampedSpring *)constraint;+ cpDataPointer data = options->data;+ cpSpaceDebugColor color = options->constraintColor;+ + cpVect a = cpTransformPoint(body_a->transform, spring->anchorA);+ cpVect b = cpTransformPoint(body_b->transform, spring->anchorB);+ + options->drawDot(5, a, color, data);+ options->drawDot(5, b, color, data);++ cpVect delta = cpvsub(b, a);+ cpFloat cos = delta.x;+ cpFloat sin = delta.y;+ cpFloat s = 1.0f/cpvlength(delta);+ + cpVect r1 = cpv(cos, -sin*s);+ cpVect r2 = cpv(sin, cos*s);+ + cpVect *verts = (cpVect *)alloca(spring_count*sizeof(cpVect));+ for(int i=0; i<spring_count; i++){+ cpVect v = spring_verts[i];+ verts[i] = cpv(cpvdot(v, r1) + a.x, cpvdot(v, r2) + a.y);+ }+ + for(int i=0; i<spring_count-1; i++){+ options->drawSegment(verts[i], verts[i + 1], color, data);+ }+ }+}++void+cpSpaceDebugDraw(cpSpace *space, cpSpaceDebugDrawOptions *options)+{+ if(options->flags & CP_SPACE_DEBUG_DRAW_SHAPES){+ cpSpaceEachShape(space, (cpSpaceShapeIteratorFunc)cpSpaceDebugDrawShape, options);+ }+ + if(options->flags & CP_SPACE_DEBUG_DRAW_CONSTRAINTS){+ cpSpaceEachConstraint(space, (cpSpaceConstraintIteratorFunc)cpSpaceDebugDrawConstraint, options);+ }+ + if(options->flags & CP_SPACE_DEBUG_DRAW_COLLISION_POINTS){+ cpArray *arbiters = space->arbiters;+ cpSpaceDebugColor color = options->collisionPointColor;+ cpSpaceDebugDrawSegmentImpl draw_seg = options->drawSegment;+ cpDataPointer data = options->data;+ + for(int i=0; i<arbiters->num; i++){+ cpArbiter *arb = (cpArbiter*)arbiters->arr[i];+ cpVect n = arb->n;+ + for(int j=0; j<arb->count; j++){+ cpVect p1 = cpvadd(arb->body_a->p, arb->contacts[j].r1);+ cpVect p2 = cpvadd(arb->body_b->p, arb->contacts[j].r2);+ + cpFloat d = 2.0f;+ cpVect a = cpvadd(p1, cpvmult(n, -d));+ cpVect b = cpvadd(p2, cpvmult(n, d));+ draw_seg(a, b, color, data);+ }+ }+ }+}++#endif
+ Chipmunk2D-7.0.2/src/cpSpaceHash.c view
@@ -0,0 +1,634 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"+#include "prime.h"++typedef struct cpSpaceHashBin cpSpaceHashBin;+typedef struct cpHandle cpHandle;++struct cpSpaceHash {+ cpSpatialIndex spatialIndex;+ + int numcells;+ cpFloat celldim;+ + cpSpaceHashBin **table;+ cpHashSet *handleSet;+ + cpSpaceHashBin *pooledBins;+ cpArray *pooledHandles;+ cpArray *allocatedBuffers;+ + cpTimestamp stamp;+};+++//MARK: Handle Functions++struct cpHandle {+ void *obj;+ int retain;+ cpTimestamp stamp;+};++static cpHandle*+cpHandleInit(cpHandle *hand, void *obj)+{+ hand->obj = obj;+ hand->retain = 0;+ hand->stamp = 0;+ + return hand;+}++static inline void cpHandleRetain(cpHandle *hand){hand->retain++;}++static inline void+cpHandleRelease(cpHandle *hand, cpArray *pooledHandles)+{+ hand->retain--;+ if(hand->retain == 0) cpArrayPush(pooledHandles, hand);+}++static int handleSetEql(void *obj, cpHandle *hand){return (obj == hand->obj);}++static void *+handleSetTrans(void *obj, cpSpaceHash *hash)+{+ if(hash->pooledHandles->num == 0){+ // handle pool is exhausted, make more+ int count = CP_BUFFER_BYTES/sizeof(cpHandle);+ cpAssertHard(count, "Internal Error: Buffer size is too small.");+ + cpHandle *buffer = (cpHandle *)cpcalloc(1, CP_BUFFER_BYTES);+ cpArrayPush(hash->allocatedBuffers, buffer);+ + for(int i=0; i<count; i++) cpArrayPush(hash->pooledHandles, buffer + i);+ }+ + cpHandle *hand = cpHandleInit((cpHandle *)cpArrayPop(hash->pooledHandles), obj);+ cpHandleRetain(hand);+ + return hand;+}++//MARK: Bin Functions++struct cpSpaceHashBin {+ cpHandle *handle;+ cpSpaceHashBin *next;+};++static inline void+recycleBin(cpSpaceHash *hash, cpSpaceHashBin *bin)+{+ bin->next = hash->pooledBins;+ hash->pooledBins = bin;+}++static inline void+clearTableCell(cpSpaceHash *hash, int idx)+{+ cpSpaceHashBin *bin = hash->table[idx];+ while(bin){+ cpSpaceHashBin *next = bin->next;+ + cpHandleRelease(bin->handle, hash->pooledHandles);+ recycleBin(hash, bin);+ + bin = next;+ }+ + hash->table[idx] = NULL;+}++static void+clearTable(cpSpaceHash *hash)+{+ for(int i=0; i<hash->numcells; i++) clearTableCell(hash, i);+}++// Get a recycled or new bin.+static inline cpSpaceHashBin *+getEmptyBin(cpSpaceHash *hash)+{+ cpSpaceHashBin *bin = hash->pooledBins;+ + if(bin){+ hash->pooledBins = bin->next;+ return bin;+ } else {+ // Pool is exhausted, make more+ int count = CP_BUFFER_BYTES/sizeof(cpSpaceHashBin);+ cpAssertHard(count, "Internal Error: Buffer size is too small.");+ + cpSpaceHashBin *buffer = (cpSpaceHashBin *)cpcalloc(1, CP_BUFFER_BYTES);+ cpArrayPush(hash->allocatedBuffers, buffer);+ + // push all but the first one, return the first instead+ for(int i=1; i<count; i++) recycleBin(hash, buffer + i);+ return buffer;+ }+}++//MARK: Memory Management Functions++cpSpaceHash *+cpSpaceHashAlloc(void)+{+ return (cpSpaceHash *)cpcalloc(1, sizeof(cpSpaceHash));+}++// Frees the old table, and allocate a new one.+static void+cpSpaceHashAllocTable(cpSpaceHash *hash, int numcells)+{+ cpfree(hash->table);+ + hash->numcells = numcells;+ hash->table = (cpSpaceHashBin **)cpcalloc(numcells, sizeof(cpSpaceHashBin *));+}++static inline cpSpatialIndexClass *Klass();++cpSpatialIndex *+cpSpaceHashInit(cpSpaceHash *hash, cpFloat celldim, int numcells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)+{+ cpSpatialIndexInit((cpSpatialIndex *)hash, Klass(), bbfunc, staticIndex);+ + cpSpaceHashAllocTable(hash, next_prime(numcells));+ hash->celldim = celldim;+ + hash->handleSet = cpHashSetNew(0, (cpHashSetEqlFunc)handleSetEql);+ + hash->pooledHandles = cpArrayNew(0);+ + hash->pooledBins = NULL;+ hash->allocatedBuffers = cpArrayNew(0);+ + hash->stamp = 1;+ + return (cpSpatialIndex *)hash;+}++cpSpatialIndex *+cpSpaceHashNew(cpFloat celldim, int cells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)+{+ return cpSpaceHashInit(cpSpaceHashAlloc(), celldim, cells, bbfunc, staticIndex);+}++static void+cpSpaceHashDestroy(cpSpaceHash *hash)+{+ if(hash->table) clearTable(hash);+ cpfree(hash->table);+ + cpHashSetFree(hash->handleSet);+ + cpArrayFreeEach(hash->allocatedBuffers, cpfree);+ cpArrayFree(hash->allocatedBuffers);+ cpArrayFree(hash->pooledHandles);+}++//MARK: Helper Functions++static inline cpBool+containsHandle(cpSpaceHashBin *bin, cpHandle *hand)+{+ while(bin){+ if(bin->handle == hand) return cpTrue;+ bin = bin->next;+ }+ + return cpFalse;+}++// The hash function itself.+static inline cpHashValue+hash_func(cpHashValue x, cpHashValue y, cpHashValue n)+{+ return (x*1640531513ul ^ y*2654435789ul) % n;+}++// Much faster than (int)floor(f)+// Profiling showed floor() to be a sizable performance hog+static inline int+floor_int(cpFloat f)+{+ int i = (int)f;+ return (f < 0.0f && f != i ? i - 1 : i);+}++static inline void+hashHandle(cpSpaceHash *hash, cpHandle *hand, cpBB bb)+{+ // Find the dimensions in cell coordinates.+ cpFloat dim = hash->celldim;+ int l = floor_int(bb.l/dim); // Fix by ShiftZ+ int r = floor_int(bb.r/dim);+ int b = floor_int(bb.b/dim);+ int t = floor_int(bb.t/dim);+ + int n = hash->numcells;+ for(int i=l; i<=r; i++){+ for(int j=b; j<=t; j++){+ cpHashValue idx = hash_func(i,j,n);+ cpSpaceHashBin *bin = hash->table[idx];+ + // Don't add an object twice to the same cell.+ if(containsHandle(bin, hand)) continue;++ cpHandleRetain(hand);+ // Insert a new bin for the handle in this cell.+ cpSpaceHashBin *newBin = getEmptyBin(hash);+ newBin->handle = hand;+ newBin->next = bin;+ hash->table[idx] = newBin;+ }+ }+}++//MARK: Basic Operations++static void+cpSpaceHashInsert(cpSpaceHash *hash, void *obj, cpHashValue hashid)+{+ cpHandle *hand = (cpHandle *)cpHashSetInsert(hash->handleSet, hashid, obj, (cpHashSetTransFunc)handleSetTrans, hash);+ hashHandle(hash, hand, hash->spatialIndex.bbfunc(obj));+}++static void+cpSpaceHashRehashObject(cpSpaceHash *hash, void *obj, cpHashValue hashid)+{+ cpHandle *hand = (cpHandle *)cpHashSetRemove(hash->handleSet, hashid, obj);+ + if(hand){+ hand->obj = NULL;+ cpHandleRelease(hand, hash->pooledHandles);+ + cpSpaceHashInsert(hash, obj, hashid);+ }+}++static void+rehash_helper(cpHandle *hand, cpSpaceHash *hash)+{+ hashHandle(hash, hand, hash->spatialIndex.bbfunc(hand->obj));+}++static void+cpSpaceHashRehash(cpSpaceHash *hash)+{+ clearTable(hash);+ cpHashSetEach(hash->handleSet, (cpHashSetIteratorFunc)rehash_helper, hash);+}++static void+cpSpaceHashRemove(cpSpaceHash *hash, void *obj, cpHashValue hashid)+{+ cpHandle *hand = (cpHandle *)cpHashSetRemove(hash->handleSet, hashid, obj);+ + if(hand){+ hand->obj = NULL;+ cpHandleRelease(hand, hash->pooledHandles);+ }+}++typedef struct eachContext {+ cpSpatialIndexIteratorFunc func;+ void *data;+} eachContext;++static void eachHelper(cpHandle *hand, eachContext *context){context->func(hand->obj, context->data);}++static void+cpSpaceHashEach(cpSpaceHash *hash, cpSpatialIndexIteratorFunc func, void *data)+{+ eachContext context = {func, data};+ cpHashSetEach(hash->handleSet, (cpHashSetIteratorFunc)eachHelper, &context);+}++static void+remove_orphaned_handles(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr)+{+ cpSpaceHashBin *bin = *bin_ptr;+ while(bin){+ cpHandle *hand = bin->handle;+ cpSpaceHashBin *next = bin->next;+ + if(!hand->obj){+ // orphaned handle, unlink and recycle the bin+ (*bin_ptr) = bin->next;+ recycleBin(hash, bin);+ + cpHandleRelease(hand, hash->pooledHandles);+ } else {+ bin_ptr = &bin->next;+ }+ + bin = next;+ }+}++//MARK: Query Functions++static inline void+query_helper(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr, void *obj, cpSpatialIndexQueryFunc func, void *data)+{+ restart:+ for(cpSpaceHashBin *bin = *bin_ptr; bin; bin = bin->next){+ cpHandle *hand = bin->handle;+ void *other = hand->obj;+ + if(hand->stamp == hash->stamp || obj == other){+ continue;+ } else if(other){+ func(obj, other, 0, data);+ hand->stamp = hash->stamp;+ } else {+ // The object for this handle has been removed+ // cleanup this cell and restart the query+ remove_orphaned_handles(hash, bin_ptr);+ goto restart; // GCC not smart enough/able to tail call an inlined function.+ }+ }+}++static void+cpSpaceHashQuery(cpSpaceHash *hash, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data)+{+ // Get the dimensions in cell coordinates.+ cpFloat dim = hash->celldim;+ int l = floor_int(bb.l/dim); // Fix by ShiftZ+ int r = floor_int(bb.r/dim);+ int b = floor_int(bb.b/dim);+ int t = floor_int(bb.t/dim);+ + int n = hash->numcells;+ cpSpaceHashBin **table = hash->table;+ + // Iterate over the cells and query them.+ for(int i=l; i<=r; i++){+ for(int j=b; j<=t; j++){+ query_helper(hash, &table[hash_func(i,j,n)], obj, func, data);+ }+ }+ + hash->stamp++;+}++// Similar to struct eachPair above.+typedef struct queryRehashContext {+ cpSpaceHash *hash;+ cpSpatialIndexQueryFunc func;+ void *data;+} queryRehashContext;++// Hashset iterator func used with cpSpaceHashQueryRehash().+static void+queryRehash_helper(cpHandle *hand, queryRehashContext *context)+{+ cpSpaceHash *hash = context->hash;+ cpSpatialIndexQueryFunc func = context->func;+ void *data = context->data;++ cpFloat dim = hash->celldim;+ int n = hash->numcells;++ void *obj = hand->obj;+ cpBB bb = hash->spatialIndex.bbfunc(obj);++ int l = floor_int(bb.l/dim);+ int r = floor_int(bb.r/dim);+ int b = floor_int(bb.b/dim);+ int t = floor_int(bb.t/dim);+ + cpSpaceHashBin **table = hash->table;++ for(int i=l; i<=r; i++){+ for(int j=b; j<=t; j++){+ cpHashValue idx = hash_func(i,j,n);+ cpSpaceHashBin *bin = table[idx];+ + if(containsHandle(bin, hand)) continue;+ + cpHandleRetain(hand); // this MUST be done first in case the object is removed in func()+ query_helper(hash, &bin, obj, func, data);+ + cpSpaceHashBin *newBin = getEmptyBin(hash);+ newBin->handle = hand;+ newBin->next = bin;+ table[idx] = newBin;+ }+ }+ + // Increment the stamp for each object hashed.+ hash->stamp++;+}++static void+cpSpaceHashReindexQuery(cpSpaceHash *hash, cpSpatialIndexQueryFunc func, void *data)+{+ clearTable(hash);+ + queryRehashContext context = {hash, func, data};+ cpHashSetEach(hash->handleSet, (cpHashSetIteratorFunc)queryRehash_helper, &context);+ + cpSpatialIndexCollideStatic((cpSpatialIndex *)hash, hash->spatialIndex.staticIndex, func, data);+}++static inline cpFloat+segmentQuery_helper(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr, void *obj, cpSpatialIndexSegmentQueryFunc func, void *data)+{+ cpFloat t = 1.0f;+ + restart:+ for(cpSpaceHashBin *bin = *bin_ptr; bin; bin = bin->next){+ cpHandle *hand = bin->handle;+ void *other = hand->obj;+ + // Skip over certain conditions+ if(hand->stamp == hash->stamp){+ continue;+ } else if(other){+ t = cpfmin(t, func(obj, other, data));+ hand->stamp = hash->stamp;+ } else {+ // The object for this handle has been removed+ // cleanup this cell and restart the query+ remove_orphaned_handles(hash, bin_ptr);+ goto restart; // GCC not smart enough/able to tail call an inlined function.+ }+ }+ + return t;+}++// modified from http://playtechs.blogspot.com/2007/03/raytracing-on-grid.html+static void+cpSpaceHashSegmentQuery(cpSpaceHash *hash, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data)+{+ a = cpvmult(a, 1.0f/hash->celldim);+ b = cpvmult(b, 1.0f/hash->celldim);+ + int cell_x = floor_int(a.x), cell_y = floor_int(a.y);++ cpFloat t = 0;++ int x_inc, y_inc;+ cpFloat temp_v, temp_h;++ if (b.x > a.x){+ x_inc = 1;+ temp_h = (cpffloor(a.x + 1.0f) - a.x);+ } else {+ x_inc = -1;+ temp_h = (a.x - cpffloor(a.x));+ }++ if (b.y > a.y){+ y_inc = 1;+ temp_v = (cpffloor(a.y + 1.0f) - a.y);+ } else {+ y_inc = -1;+ temp_v = (a.y - cpffloor(a.y));+ }+ + // Division by zero is *very* slow on ARM+ cpFloat dx = cpfabs(b.x - a.x), dy = cpfabs(b.y - a.y);+ cpFloat dt_dx = (dx ? 1.0f/dx : INFINITY), dt_dy = (dy ? 1.0f/dy : INFINITY);+ + // fix NANs in horizontal directions+ cpFloat next_h = (temp_h ? temp_h*dt_dx : dt_dx);+ cpFloat next_v = (temp_v ? temp_v*dt_dy : dt_dy);+ + int n = hash->numcells;+ cpSpaceHashBin **table = hash->table;++ while(t < t_exit){+ cpHashValue idx = hash_func(cell_x, cell_y, n);+ t_exit = cpfmin(t_exit, segmentQuery_helper(hash, &table[idx], obj, func, data));++ if (next_v < next_h){+ cell_y += y_inc;+ t = next_v;+ next_v += dt_dy;+ } else {+ cell_x += x_inc;+ t = next_h;+ next_h += dt_dx;+ }+ }+ + hash->stamp++;+}++//MARK: Misc++void+cpSpaceHashResize(cpSpaceHash *hash, cpFloat celldim, int numcells)+{+ if(hash->spatialIndex.klass != Klass()){+ cpAssertWarn(cpFalse, "Ignoring cpSpaceHashResize() call to non-cpSpaceHash spatial index.");+ return;+ }+ + clearTable(hash);+ + hash->celldim = celldim;+ cpSpaceHashAllocTable(hash, next_prime(numcells));+}++static int+cpSpaceHashCount(cpSpaceHash *hash)+{+ return cpHashSetCount(hash->handleSet);+}++static int+cpSpaceHashContains(cpSpaceHash *hash, void *obj, cpHashValue hashid)+{+ return cpHashSetFind(hash->handleSet, hashid, obj) != NULL;+}++static cpSpatialIndexClass klass = {+ (cpSpatialIndexDestroyImpl)cpSpaceHashDestroy,+ + (cpSpatialIndexCountImpl)cpSpaceHashCount,+ (cpSpatialIndexEachImpl)cpSpaceHashEach,+ (cpSpatialIndexContainsImpl)cpSpaceHashContains,+ + (cpSpatialIndexInsertImpl)cpSpaceHashInsert,+ (cpSpatialIndexRemoveImpl)cpSpaceHashRemove,+ + (cpSpatialIndexReindexImpl)cpSpaceHashRehash,+ (cpSpatialIndexReindexObjectImpl)cpSpaceHashRehashObject,+ (cpSpatialIndexReindexQueryImpl)cpSpaceHashReindexQuery,+ + (cpSpatialIndexQueryImpl)cpSpaceHashQuery,+ (cpSpatialIndexSegmentQueryImpl)cpSpaceHashSegmentQuery,+};++static inline cpSpatialIndexClass *Klass(){return &klass;}++//MARK: Debug Drawing++//#define CP_BBTREE_DEBUG_DRAW+#ifdef CP_BBTREE_DEBUG_DRAW+#include "OpenGL/gl.h"+#include "OpenGL/glu.h"+#include <GLUT/glut.h>++void+cpSpaceHashRenderDebug(cpSpatialIndex *index)+{+ if(index->klass != &klass){+ cpAssertWarn(cpFalse, "Ignoring cpSpaceHashRenderDebug() call to non-spatial hash spatial index.");+ return;+ }+ + cpSpaceHash *hash = (cpSpaceHash *)index;+ cpBB bb = cpBBNew(-320, -240, 320, 240);+ + cpFloat dim = hash->celldim;+ int n = hash->numcells;+ + int l = (int)floor(bb.l/dim);+ int r = (int)floor(bb.r/dim);+ int b = (int)floor(bb.b/dim);+ int t = (int)floor(bb.t/dim);+ + for(int i=l; i<=r; i++){+ for(int j=b; j<=t; j++){+ int cell_count = 0;+ + int index = hash_func(i,j,n);+ for(cpSpaceHashBin *bin = hash->table[index]; bin; bin = bin->next)+ cell_count++;+ + GLfloat v = 1.0f - (GLfloat)cell_count/10.0f;+ glColor3f(v,v,v);+ glRectf(i*dim, j*dim, (i + 1)*dim, (j + 1)*dim);+ }+ }+}+#endif
+ Chipmunk2D-7.0.2/src/cpSpaceQuery.c view
@@ -0,0 +1,246 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++//MARK: Nearest Point Query Functions++struct PointQueryContext {+ cpVect point;+ cpFloat maxDistance;+ cpShapeFilter filter;+ cpSpacePointQueryFunc func;+};++static cpCollisionID+NearestPointQuery(struct PointQueryContext *context, cpShape *shape, cpCollisionID id, void *data)+{+ if(+ !cpShapeFilterReject(shape->filter, context->filter)+ ){+ cpPointQueryInfo info;+ cpShapePointQuery(shape, context->point, &info);+ + if(info.shape && info.distance < context->maxDistance) context->func(shape, info.point, info.distance, info.gradient, data);+ }+ + return id;+}++void+cpSpacePointQuery(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryFunc func, void *data)+{+ struct PointQueryContext context = {point, maxDistance, filter, func};+ cpBB bb = cpBBNewForCircle(point, cpfmax(maxDistance, 0.0f));+ + cpSpaceLock(space); {+ cpSpatialIndexQuery(space->dynamicShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQuery, data);+ cpSpatialIndexQuery(space->staticShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQuery, data);+ } cpSpaceUnlock(space, cpTrue);+}++static cpCollisionID+NearestPointQueryNearest(struct PointQueryContext *context, cpShape *shape, cpCollisionID id, cpPointQueryInfo *out)+{+ if(+ !cpShapeFilterReject(shape->filter, context->filter) && !shape->sensor+ ){+ cpPointQueryInfo info;+ cpShapePointQuery(shape, context->point, &info);+ + if(info.distance < out->distance) (*out) = info;+ }+ + return id;+}++cpShape *+cpSpacePointQueryNearest(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpPointQueryInfo *out)+{+ cpPointQueryInfo info = {NULL, cpvzero, maxDistance, cpvzero};+ if(out){+ (*out) = info;+ } else {+ out = &info;+ }+ + struct PointQueryContext context = {+ point, maxDistance,+ filter,+ NULL+ };+ + cpBB bb = cpBBNewForCircle(point, cpfmax(maxDistance, 0.0f));+ cpSpatialIndexQuery(space->dynamicShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQueryNearest, out);+ cpSpatialIndexQuery(space->staticShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQueryNearest, out);+ + return (cpShape *)out->shape;+}+++//MARK: Segment Query Functions++struct SegmentQueryContext {+ cpVect start, end;+ cpFloat radius;+ cpShapeFilter filter;+ cpSpaceSegmentQueryFunc func;+};++static cpFloat+SegmentQuery(struct SegmentQueryContext *context, cpShape *shape, void *data)+{+ cpSegmentQueryInfo info;+ + if(+ !cpShapeFilterReject(shape->filter, context->filter) &&+ cpShapeSegmentQuery(shape, context->start, context->end, context->radius, &info)+ ){+ context->func(shape, info.point, info.normal, info.alpha, data);+ }+ + return 1.0f;+}++void+cpSpaceSegmentQuery(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryFunc func, void *data)+{+ struct SegmentQueryContext context = {+ start, end,+ radius,+ filter,+ func,+ };+ + cpSpaceLock(space); {+ cpSpatialIndexSegmentQuery(space->staticShapes, &context, start, end, 1.0f, (cpSpatialIndexSegmentQueryFunc)SegmentQuery, data);+ cpSpatialIndexSegmentQuery(space->dynamicShapes, &context, start, end, 1.0f, (cpSpatialIndexSegmentQueryFunc)SegmentQuery, data);+ } cpSpaceUnlock(space, cpTrue);+}++static cpFloat+SegmentQueryFirst(struct SegmentQueryContext *context, cpShape *shape, cpSegmentQueryInfo *out)+{+ cpSegmentQueryInfo info;+ + if(+ !cpShapeFilterReject(shape->filter, context->filter) && !shape->sensor &&+ cpShapeSegmentQuery(shape, context->start, context->end, context->radius, &info) &&+ info.alpha < out->alpha+ ){+ (*out) = info;+ }+ + return out->alpha;+}++cpShape *+cpSpaceSegmentQueryFirst(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSegmentQueryInfo *out)+{+ cpSegmentQueryInfo info = {NULL, end, cpvzero, 1.0f};+ if(out){+ (*out) = info;+ } else {+ out = &info;+ }+ + struct SegmentQueryContext context = {+ start, end,+ radius,+ filter,+ NULL+ };+ + cpSpatialIndexSegmentQuery(space->staticShapes, &context, start, end, 1.0f, (cpSpatialIndexSegmentQueryFunc)SegmentQueryFirst, out);+ cpSpatialIndexSegmentQuery(space->dynamicShapes, &context, start, end, out->alpha, (cpSpatialIndexSegmentQueryFunc)SegmentQueryFirst, out);+ + return (cpShape *)out->shape;+}++//MARK: BB Query Functions++struct BBQueryContext {+ cpBB bb;+ cpShapeFilter filter;+ cpSpaceBBQueryFunc func;+};++static cpCollisionID+BBQuery(struct BBQueryContext *context, cpShape *shape, cpCollisionID id, void *data)+{+ if(+ !cpShapeFilterReject(shape->filter, context->filter) &&+ cpBBIntersects(context->bb, shape->bb)+ ){+ context->func(shape, data);+ }+ + return id;+}++void+cpSpaceBBQuery(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryFunc func, void *data)+{+ struct BBQueryContext context = {bb, filter, func};+ + cpSpaceLock(space); {+ cpSpatialIndexQuery(space->dynamicShapes, &context, bb, (cpSpatialIndexQueryFunc)BBQuery, data);+ cpSpatialIndexQuery(space->staticShapes, &context, bb, (cpSpatialIndexQueryFunc)BBQuery, data);+ } cpSpaceUnlock(space, cpTrue);+}++//MARK: Shape Query Functions++struct ShapeQueryContext {+ cpSpaceShapeQueryFunc func;+ void *data;+ cpBool anyCollision;+};++// Callback from the spatial hash.+static cpCollisionID+ShapeQuery(cpShape *a, cpShape *b, cpCollisionID id, struct ShapeQueryContext *context)+{+ if(cpShapeFilterReject(a->filter, b->filter) || a == b) return id;+ + cpContactPointSet set = cpShapesCollide(a, b);+ if(set.count){+ if(context->func) context->func(b, &set, context->data);+ context->anyCollision = !(a->sensor || b->sensor);+ }+ + return id;+}++cpBool+cpSpaceShapeQuery(cpSpace *space, cpShape *shape, cpSpaceShapeQueryFunc func, void *data)+{+ cpBody *body = shape->body;+ cpBB bb = (body ? cpShapeUpdate(shape, body->transform) : shape->bb);+ struct ShapeQueryContext context = {func, data, cpFalse};+ + cpSpaceLock(space); {+ cpSpatialIndexQuery(space->dynamicShapes, shape, bb, (cpSpatialIndexQueryFunc)ShapeQuery, &context);+ cpSpatialIndexQuery(space->staticShapes, shape, bb, (cpSpatialIndexQueryFunc)ShapeQuery, &context);+ } cpSpaceUnlock(space, cpTrue);+ + return context.anyCollision;+}
+ Chipmunk2D-7.0.2/src/cpSpaceStep.c view
@@ -0,0 +1,445 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++//MARK: Post Step Callback Functions++cpPostStepCallback *+cpSpaceGetPostStepCallback(cpSpace *space, void *key)+{+ cpArray *arr = space->postStepCallbacks;+ for(int i=0; i<arr->num; i++){+ cpPostStepCallback *callback = (cpPostStepCallback *)arr->arr[i];+ if(callback && callback->key == key) return callback;+ }+ + return NULL;+}++static void PostStepDoNothing(cpSpace *space, void *obj, void *data){}++cpBool+cpSpaceAddPostStepCallback(cpSpace *space, cpPostStepFunc func, void *key, void *data)+{+ cpAssertWarn(space->locked,+ "Adding a post-step callback when the space is not locked is unnecessary. "+ "Post-step callbacks will not called until the end of the next call to cpSpaceStep() or the next query.");+ + if(!cpSpaceGetPostStepCallback(space, key)){+ cpPostStepCallback *callback = (cpPostStepCallback *)cpcalloc(1, sizeof(cpPostStepCallback));+ callback->func = (func ? func : PostStepDoNothing);+ callback->key = key;+ callback->data = data;+ + cpArrayPush(space->postStepCallbacks, callback);+ return cpTrue;+ } else {+ return cpFalse;+ }+}++//MARK: Locking Functions++void+cpSpaceLock(cpSpace *space)+{+ space->locked++;+}++void+cpSpaceUnlock(cpSpace *space, cpBool runPostStep)+{+ space->locked--;+ cpAssertHard(space->locked >= 0, "Internal Error: Space lock underflow.");+ + if(space->locked == 0){+ cpArray *waking = space->rousedBodies;+ + for(int i=0, count=waking->num; i<count; i++){+ cpSpaceActivateBody(space, (cpBody *)waking->arr[i]);+ waking->arr[i] = NULL;+ }+ + waking->num = 0;+ + if(space->locked == 0 && runPostStep && !space->skipPostStep){+ space->skipPostStep = cpTrue;+ + cpArray *arr = space->postStepCallbacks;+ for(int i=0; i<arr->num; i++){+ cpPostStepCallback *callback = (cpPostStepCallback *)arr->arr[i];+ cpPostStepFunc func = callback->func;+ + // Mark the func as NULL in case calling it calls cpSpaceRunPostStepCallbacks() again.+ // TODO: need more tests around this case I think.+ callback->func = NULL;+ if(func) func(space, callback->key, callback->data);+ + arr->arr[i] = NULL;+ cpfree(callback);+ }+ + arr->num = 0;+ space->skipPostStep = cpFalse;+ }+ }+}++//MARK: Contact Buffer Functions++struct cpContactBufferHeader {+ cpTimestamp stamp;+ cpContactBufferHeader *next;+ unsigned int numContacts;+};++#define CP_CONTACTS_BUFFER_SIZE ((CP_BUFFER_BYTES - sizeof(cpContactBufferHeader))/sizeof(struct cpContact))+typedef struct cpContactBuffer {+ cpContactBufferHeader header;+ struct cpContact contacts[CP_CONTACTS_BUFFER_SIZE];+} cpContactBuffer;++static cpContactBufferHeader *+cpSpaceAllocContactBuffer(cpSpace *space)+{+ cpContactBuffer *buffer = (cpContactBuffer *)cpcalloc(1, sizeof(cpContactBuffer));+ cpArrayPush(space->allocatedBuffers, buffer);+ return (cpContactBufferHeader *)buffer;+}++static cpContactBufferHeader *+cpContactBufferHeaderInit(cpContactBufferHeader *header, cpTimestamp stamp, cpContactBufferHeader *splice)+{+ header->stamp = stamp;+ header->next = (splice ? splice->next : header);+ header->numContacts = 0;+ + return header;+}++void+cpSpacePushFreshContactBuffer(cpSpace *space)+{+ cpTimestamp stamp = space->stamp;+ + cpContactBufferHeader *head = space->contactBuffersHead;+ + if(!head){+ // No buffers have been allocated, make one+ space->contactBuffersHead = cpContactBufferHeaderInit(cpSpaceAllocContactBuffer(space), stamp, NULL);+ } else if(stamp - head->next->stamp > space->collisionPersistence){+ // The tail buffer is available, rotate the ring+ cpContactBufferHeader *tail = head->next;+ space->contactBuffersHead = cpContactBufferHeaderInit(tail, stamp, tail);+ } else {+ // Allocate a new buffer and push it into the ring+ cpContactBufferHeader *buffer = cpContactBufferHeaderInit(cpSpaceAllocContactBuffer(space), stamp, head);+ space->contactBuffersHead = head->next = buffer;+ }+}+++struct cpContact *+cpContactBufferGetArray(cpSpace *space)+{+ if(space->contactBuffersHead->numContacts + CP_MAX_CONTACTS_PER_ARBITER > CP_CONTACTS_BUFFER_SIZE){+ // contact buffer could overflow on the next collision, push a fresh one.+ cpSpacePushFreshContactBuffer(space);+ }+ + cpContactBufferHeader *head = space->contactBuffersHead;+ return ((cpContactBuffer *)head)->contacts + head->numContacts;+}++void+cpSpacePushContacts(cpSpace *space, int count)+{+ cpAssertHard(count <= CP_MAX_CONTACTS_PER_ARBITER, "Internal Error: Contact buffer overflow!");+ space->contactBuffersHead->numContacts += count;+}++static void+cpSpacePopContacts(cpSpace *space, int count){+ space->contactBuffersHead->numContacts -= count;+}++//MARK: Collision Detection Functions++static void *+cpSpaceArbiterSetTrans(cpShape **shapes, cpSpace *space)+{+ if(space->pooledArbiters->num == 0){+ // arbiter pool is exhausted, make more+ int count = CP_BUFFER_BYTES/sizeof(cpArbiter);+ cpAssertHard(count, "Internal Error: Buffer size too small.");+ + cpArbiter *buffer = (cpArbiter *)cpcalloc(1, CP_BUFFER_BYTES);+ cpArrayPush(space->allocatedBuffers, buffer);+ + for(int i=0; i<count; i++) cpArrayPush(space->pooledArbiters, buffer + i);+ }+ + return cpArbiterInit((cpArbiter *)cpArrayPop(space->pooledArbiters), shapes[0], shapes[1]);+}++static inline cpBool+QueryRejectConstraint(cpBody *a, cpBody *b)+{+ CP_BODY_FOREACH_CONSTRAINT(a, constraint){+ if(+ !constraint->collideBodies && (+ (constraint->a == a && constraint->b == b) ||+ (constraint->a == b && constraint->b == a)+ )+ ) return cpTrue;+ }+ + return cpFalse;+}++static inline cpBool+QueryReject(cpShape *a, cpShape *b)+{+ return (+ // BBoxes must overlap+ !cpBBIntersects(a->bb, b->bb)+ // Don't collide shapes attached to the same body.+ || a->body == b->body+ // Don't collide shapes that are filtered.+ || cpShapeFilterReject(a->filter, b->filter)+ // Don't collide bodies if they have a constraint with collideBodies == cpFalse.+ || QueryRejectConstraint(a->body, b->body)+ );+}++// Callback from the spatial hash.+cpCollisionID+cpSpaceCollideShapes(cpShape *a, cpShape *b, cpCollisionID id, cpSpace *space)+{+ // Reject any of the simple cases+ if(QueryReject(a,b)) return id;+ + // Narrow-phase collision detection.+ struct cpCollisionInfo info = cpCollide(a, b, id, cpContactBufferGetArray(space));+ + if(info.count == 0) return info.id; // Shapes are not colliding.+ cpSpacePushContacts(space, info.count);+ + // Get an arbiter from space->arbiterSet for the two shapes.+ // This is where the persistant contact magic comes from.+ const cpShape *shape_pair[] = {info.a, info.b};+ cpHashValue arbHashID = CP_HASH_PAIR((cpHashValue)info.a, (cpHashValue)info.b);+ cpArbiter *arb = (cpArbiter *)cpHashSetInsert(space->cachedArbiters, arbHashID, shape_pair, (cpHashSetTransFunc)cpSpaceArbiterSetTrans, space);+ cpArbiterUpdate(arb, &info, space);+ + cpCollisionHandler *handler = arb->handler;+ + // Call the begin function first if it's the first step+ if(arb->state == CP_ARBITER_STATE_FIRST_COLLISION && !handler->beginFunc(arb, space, handler->userData)){+ cpArbiterIgnore(arb); // permanently ignore the collision until separation+ }+ + if(+ // Ignore the arbiter if it has been flagged+ (arb->state != CP_ARBITER_STATE_IGNORE) && + // Call preSolve+ handler->preSolveFunc(arb, space, handler->userData) &&+ // Check (again) in case the pre-solve() callback called cpArbiterIgnored().+ arb->state != CP_ARBITER_STATE_IGNORE &&+ // Process, but don't add collisions for sensors.+ !(a->sensor || b->sensor) &&+ // Don't process collisions between two infinite mass bodies.+ // This includes collisions between two kinematic bodies, or a kinematic body and a static body.+ !(a->body->m == INFINITY && b->body->m == INFINITY)+ ){+ cpArrayPush(space->arbiters, arb);+ } else {+ cpSpacePopContacts(space, info.count);+ + arb->contacts = NULL;+ arb->count = 0;+ + // Normally arbiters are set as used after calling the post-solve callback.+ // However, post-solve() callbacks are not called for sensors or arbiters rejected from pre-solve.+ if(arb->state != CP_ARBITER_STATE_IGNORE) arb->state = CP_ARBITER_STATE_NORMAL;+ }+ + // Time stamp the arbiter so we know it was used recently.+ arb->stamp = space->stamp;+ return info.id;+}++// Hashset filter func to throw away old arbiters.+cpBool+cpSpaceArbiterSetFilter(cpArbiter *arb, cpSpace *space)+{+ cpTimestamp ticks = space->stamp - arb->stamp;+ + cpBody *a = arb->body_a, *b = arb->body_b;+ + // TODO: should make an arbiter state for this so it doesn't require filtering arbiters for dangling body pointers on body removal.+ // Preserve arbiters on sensors and rejected arbiters for sleeping objects.+ // This prevents errant separate callbacks from happenening.+ if(+ (cpBodyGetType(a) == CP_BODY_TYPE_STATIC || cpBodyIsSleeping(a)) &&+ (cpBodyGetType(b) == CP_BODY_TYPE_STATIC || cpBodyIsSleeping(b))+ ){+ return cpTrue;+ }+ + // Arbiter was used last frame, but not this one+ if(ticks >= 1 && arb->state != CP_ARBITER_STATE_CACHED){+ arb->state = CP_ARBITER_STATE_CACHED;+ cpCollisionHandler *handler = arb->handler;+ handler->separateFunc(arb, space, handler->userData);+ }+ + if(ticks >= space->collisionPersistence){+ arb->contacts = NULL;+ arb->count = 0;+ + cpArrayPush(space->pooledArbiters, arb);+ return cpFalse;+ }+ + return cpTrue;+}++//MARK: All Important cpSpaceStep() Function++ void+cpShapeUpdateFunc(cpShape *shape, void *unused)+{+ cpShapeCacheBB(shape);+}++void+cpSpaceStep(cpSpace *space, cpFloat dt)+{+ // don't step if the timestep is 0!+ if(dt == 0.0f) return;+ + space->stamp++;+ + cpFloat prev_dt = space->curr_dt;+ space->curr_dt = dt;+ + cpArray *bodies = space->dynamicBodies;+ cpArray *constraints = space->constraints;+ cpArray *arbiters = space->arbiters;+ + // Reset and empty the arbiter lists.+ for(int i=0; i<arbiters->num; i++){+ cpArbiter *arb = (cpArbiter *)arbiters->arr[i];+ arb->state = CP_ARBITER_STATE_NORMAL;+ + // If both bodies are awake, unthread the arbiter from the contact graph.+ if(!cpBodyIsSleeping(arb->body_a) && !cpBodyIsSleeping(arb->body_b)){+ cpArbiterUnthread(arb);+ }+ }+ arbiters->num = 0;++ cpSpaceLock(space); {+ // Integrate positions+ for(int i=0; i<bodies->num; i++){+ cpBody *body = (cpBody *)bodies->arr[i];+ body->position_func(body, dt);+ }+ + // Find colliding pairs.+ cpSpacePushFreshContactBuffer(space);+ cpSpatialIndexEach(space->dynamicShapes, (cpSpatialIndexIteratorFunc)cpShapeUpdateFunc, NULL);+ cpSpatialIndexReindexQuery(space->dynamicShapes, (cpSpatialIndexQueryFunc)cpSpaceCollideShapes, space);+ } cpSpaceUnlock(space, cpFalse);+ + // Rebuild the contact graph (and detect sleeping components if sleeping is enabled)+ cpSpaceProcessComponents(space, dt);+ + cpSpaceLock(space); {+ // Clear out old cached arbiters and call separate callbacks+ cpHashSetFilter(space->cachedArbiters, (cpHashSetFilterFunc)cpSpaceArbiterSetFilter, space);++ // Prestep the arbiters and constraints.+ cpFloat slop = space->collisionSlop;+ cpFloat biasCoef = 1.0f - cpfpow(space->collisionBias, dt);+ for(int i=0; i<arbiters->num; i++){+ cpArbiterPreStep((cpArbiter *)arbiters->arr[i], dt, slop, biasCoef);+ }++ for(int i=0; i<constraints->num; i++){+ cpConstraint *constraint = (cpConstraint *)constraints->arr[i];+ + cpConstraintPreSolveFunc preSolve = constraint->preSolve;+ if(preSolve) preSolve(constraint, space);+ + constraint->klass->preStep(constraint, dt);+ }+ + // Integrate velocities.+ cpFloat damping = cpfpow(space->damping, dt);+ cpVect gravity = space->gravity;+ for(int i=0; i<bodies->num; i++){+ cpBody *body = (cpBody *)bodies->arr[i];+ body->velocity_func(body, gravity, damping, dt);+ }+ + // Apply cached impulses+ cpFloat dt_coef = (prev_dt == 0.0f ? 0.0f : dt/prev_dt);+ for(int i=0; i<arbiters->num; i++){+ cpArbiterApplyCachedImpulse((cpArbiter *)arbiters->arr[i], dt_coef);+ }+ + for(int i=0; i<constraints->num; i++){+ cpConstraint *constraint = (cpConstraint *)constraints->arr[i];+ constraint->klass->applyCachedImpulse(constraint, dt_coef);+ }+ + // Run the impulse solver.+ for(int i=0; i<space->iterations; i++){+ for(int j=0; j<arbiters->num; j++){+ cpArbiterApplyImpulse((cpArbiter *)arbiters->arr[j]);+ }+ + for(int j=0; j<constraints->num; j++){+ cpConstraint *constraint = (cpConstraint *)constraints->arr[j];+ constraint->klass->applyImpulse(constraint, dt);+ }+ }+ + // Run the constraint post-solve callbacks+ for(int i=0; i<constraints->num; i++){+ cpConstraint *constraint = (cpConstraint *)constraints->arr[i];+ + cpConstraintPostSolveFunc postSolve = constraint->postSolve;+ if(postSolve) postSolve(constraint, space);+ }+ + // run the post-solve callbacks+ for(int i=0; i<arbiters->num; i++){+ cpArbiter *arb = (cpArbiter *) arbiters->arr[i];+ + cpCollisionHandler *handler = arb->handler;+ handler->postSolveFunc(arb, space, handler->userData);+ }+ } cpSpaceUnlock(space, cpTrue);+}
+ Chipmunk2D-7.0.2/src/cpSpatialIndex.c view
@@ -0,0 +1,69 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++void+cpSpatialIndexFree(cpSpatialIndex *index)+{+ if(index){+ cpSpatialIndexDestroy(index);+ cpfree(index);+ }+}++cpSpatialIndex *+cpSpatialIndexInit(cpSpatialIndex *index, cpSpatialIndexClass *klass, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)+{+ index->klass = klass;+ index->bbfunc = bbfunc;+ index->staticIndex = staticIndex;+ + if(staticIndex){+ cpAssertHard(!staticIndex->dynamicIndex, "This static index is already associated with a dynamic index.");+ staticIndex->dynamicIndex = index;+ }+ + return index;+}++typedef struct dynamicToStaticContext {+ cpSpatialIndexBBFunc bbfunc;+ cpSpatialIndex *staticIndex;+ cpSpatialIndexQueryFunc queryFunc;+ void *data;+} dynamicToStaticContext;++static void+dynamicToStaticIter(void *obj, dynamicToStaticContext *context)+{+ cpSpatialIndexQuery(context->staticIndex, obj, context->bbfunc(obj), context->queryFunc, context->data);+}++void+cpSpatialIndexCollideStatic(cpSpatialIndex *dynamicIndex, cpSpatialIndex *staticIndex, cpSpatialIndexQueryFunc func, void *data)+{+ if(staticIndex && cpSpatialIndexCount(staticIndex) > 0){+ dynamicToStaticContext context = {dynamicIndex->bbfunc, staticIndex, func, data};+ cpSpatialIndexEach(dynamicIndex, (cpSpatialIndexIteratorFunc)dynamicToStaticIter, &context);+ }+}+
+ Chipmunk2D-7.0.2/src/cpSweep1D.c view
@@ -0,0 +1,254 @@+/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software+ * + * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ * + * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#include "chipmunk/chipmunk_private.h"++static inline cpSpatialIndexClass *Klass();++//MARK: Basic Structures++typedef struct Bounds {+ cpFloat min, max;+} Bounds;++typedef struct TableCell {+ void *obj;+ Bounds bounds;+} TableCell;++struct cpSweep1D+{+ cpSpatialIndex spatialIndex;+ + int num;+ int max;+ TableCell *table;+};++static inline cpBool+BoundsOverlap(Bounds a, Bounds b)+{+ return (a.min <= b.max && b.min <= a.max);+}++static inline Bounds+BBToBounds(cpSweep1D *sweep, cpBB bb)+{+ Bounds bounds = {bb.l, bb.r};+ return bounds;+}++static inline TableCell+MakeTableCell(cpSweep1D *sweep, void *obj)+{+ TableCell cell = {obj, BBToBounds(sweep, sweep->spatialIndex.bbfunc(obj))};+ return cell;+}++//MARK: Memory Management Functions++cpSweep1D *+cpSweep1DAlloc(void)+{+ return (cpSweep1D *)cpcalloc(1, sizeof(cpSweep1D));+}++static void+ResizeTable(cpSweep1D *sweep, int size)+{+ sweep->max = size;+ sweep->table = (TableCell *)cprealloc(sweep->table, size*sizeof(TableCell));+}++cpSpatialIndex *+cpSweep1DInit(cpSweep1D *sweep, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)+{+ cpSpatialIndexInit((cpSpatialIndex *)sweep, Klass(), bbfunc, staticIndex);+ + sweep->num = 0;+ ResizeTable(sweep, 32);+ + return (cpSpatialIndex *)sweep;+}++cpSpatialIndex *+cpSweep1DNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)+{+ return cpSweep1DInit(cpSweep1DAlloc(), bbfunc, staticIndex);+}++static void+cpSweep1DDestroy(cpSweep1D *sweep)+{+ cpfree(sweep->table);+ sweep->table = NULL;+}++//MARK: Misc++static int+cpSweep1DCount(cpSweep1D *sweep)+{+ return sweep->num;+}++static void+cpSweep1DEach(cpSweep1D *sweep, cpSpatialIndexIteratorFunc func, void *data)+{+ TableCell *table = sweep->table;+ for(int i=0, count=sweep->num; i<count; i++) func(table[i].obj, data);+}++static int+cpSweep1DContains(cpSweep1D *sweep, void *obj, cpHashValue hashid)+{+ TableCell *table = sweep->table;+ for(int i=0, count=sweep->num; i<count; i++){+ if(table[i].obj == obj) return cpTrue;+ }+ + return cpFalse;+}++//MARK: Basic Operations++static void+cpSweep1DInsert(cpSweep1D *sweep, void *obj, cpHashValue hashid)+{+ if(sweep->num == sweep->max) ResizeTable(sweep, sweep->max*2);+ + sweep->table[sweep->num] = MakeTableCell(sweep, obj);+ sweep->num++;+}++static void+cpSweep1DRemove(cpSweep1D *sweep, void *obj, cpHashValue hashid)+{+ TableCell *table = sweep->table;+ for(int i=0, count=sweep->num; i<count; i++){+ if(table[i].obj == obj){+ int num = --sweep->num;+ + table[i] = table[num];+ table[num].obj = NULL;+ + return;+ }+ }+}++//MARK: Reindexing Functions++static void+cpSweep1DReindexObject(cpSweep1D *sweep, void *obj, cpHashValue hashid)+{+ // Nothing to do here+}++static void+cpSweep1DReindex(cpSweep1D *sweep)+{+ // Nothing to do here+ // Could perform a sort, but queries are not accelerated anyway.+}++//MARK: Query Functions++static void+cpSweep1DQuery(cpSweep1D *sweep, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data)+{+ // Implementing binary search here would allow you to find an upper limit+ // but not a lower limit. Probably not worth the hassle.+ + Bounds bounds = BBToBounds(sweep, bb);+ + TableCell *table = sweep->table;+ for(int i=0, count=sweep->num; i<count; i++){+ TableCell cell = table[i];+ if(BoundsOverlap(bounds, cell.bounds) && obj != cell.obj) func(obj, cell.obj, 0, data);+ }+}++static void+cpSweep1DSegmentQuery(cpSweep1D *sweep, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data)+{+ cpBB bb = cpBBExpand(cpBBNew(a.x, a.y, a.x, a.y), b);+ Bounds bounds = BBToBounds(sweep, bb);+ + TableCell *table = sweep->table;+ for(int i=0, count=sweep->num; i<count; i++){+ TableCell cell = table[i];+ if(BoundsOverlap(bounds, cell.bounds)) func(obj, cell.obj, data);+ }+}++//MARK: Reindex/Query++static int+TableSort(TableCell *a, TableCell *b)+{+ return (a->bounds.min < b->bounds.min ? -1 : (a->bounds.min > b->bounds.min ? 1 : 0));+}++static void+cpSweep1DReindexQuery(cpSweep1D *sweep, cpSpatialIndexQueryFunc func, void *data)+{+ TableCell *table = sweep->table;+ int count = sweep->num;+ + // Update bounds and sort+ for(int i=0; i<count; i++) table[i] = MakeTableCell(sweep, table[i].obj);+ qsort(table, count, sizeof(TableCell), (int (*)(const void *, const void *))TableSort); // TODO: use insertion sort instead+ + for(int i=0; i<count; i++){+ TableCell cell = table[i];+ cpFloat max = cell.bounds.max;+ + for(int j=i+1; table[j].bounds.min < max && j<count; j++){+ func(cell.obj, table[j].obj, 0, data);+ }+ }+ + // Reindex query is also responsible for colliding against the static index.+ // Fortunately there is a helper function for that.+ cpSpatialIndexCollideStatic((cpSpatialIndex *)sweep, sweep->spatialIndex.staticIndex, func, data);+}++static cpSpatialIndexClass klass = {+ (cpSpatialIndexDestroyImpl)cpSweep1DDestroy,+ + (cpSpatialIndexCountImpl)cpSweep1DCount,+ (cpSpatialIndexEachImpl)cpSweep1DEach,+ (cpSpatialIndexContainsImpl)cpSweep1DContains,+ + (cpSpatialIndexInsertImpl)cpSweep1DInsert,+ (cpSpatialIndexRemoveImpl)cpSweep1DRemove,+ + (cpSpatialIndexReindexImpl)cpSweep1DReindex,+ (cpSpatialIndexReindexObjectImpl)cpSweep1DReindexObject,+ (cpSpatialIndexReindexQueryImpl)cpSweep1DReindexQuery,+ + (cpSpatialIndexQueryImpl)cpSweep1DQuery,+ (cpSpatialIndexSegmentQueryImpl)cpSweep1DSegmentQuery,+};++static inline cpSpatialIndexClass *Klass(){return &klass;}+
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Cthulhu (c) 2018++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Cthulhu nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,3 @@+# chiphunk++Chiphunk is a Haskell bindings for Chipmunk2D physics library. See `Chiphunk.Low` module for documentation.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/Main.hs view
@@ -0,0 +1,168 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+module Main where++import Chiphunk.Low+import Data.Functor+import Text.Printf (printf)+import Control.Monad+import Control.Concurrent.MVar+import Control.Concurrent (threadDelay)+import Control.Concurrent.Async++import qualified Graphics.NanoVG.Simple as N+import qualified Graphics.NanoVG.Picture as N+import qualified NanoVG as NVG+import Data.IORef++main :: IO ()+main = do+ dm <- newEmptyMVar+ race_+ (simulate dm)+ (display dm)++simulate :: MVar [VisObj] -> IO ()+simulate dm = do+ let gravity = Vect 0 (-100)+ -- Create an empty space.+ space <- spaceNew+ spaceGravity space $= gravity++ static <- get $ spaceStaticBody space++ -- Add a static line segment shape for the ground.+ -- We'll make it slightly tilted so the ball will roll off.+ -- We attach it to a static body to tell Chipmunk it shouldn't be movable.+ let (segA, segB) = (Vect (-20) (-5), Vect 20 (-25))+ ground <- segmentShapeNew static segA segB 0+ shapeElasticity ground $= 0.6+ shapeFriction ground $= 1++ spaceAddShape space ground++ -- Now let's make a ball that falls onto the line and rolls off.+ -- First we need to make a cpBody to hold the physical properties of the object.+ -- These include the mass, position, velocity, angle, etc. of the object.+ -- Then we attach collision shapes to the cpBody to give it a size and shape.++ let radius = 5+ let mass = 1+ let mass100 = 100++ -- The moment of inertia is like mass for rotation+ -- Use the cpMomentFor*() functions to help you approximate it.+ let moment = momentForCircle mass 0 radius (Vect 0 0)+ let moment100 = momentForCircle mass100 0 radius (Vect 0 0)++ -- The cpSpaceAdd*() functions return the thing that you are adding.+ -- It's convenient to create and add an object in one line.+ ballBody <- bodyNew mass moment+ spaceAddBody space ballBody++ -- Now we create the collision shape for the ball.+ -- You can create multiple collision shapes that point to the same body.+ -- They will all be attached to the body and move around to follow it.+ ballShape <- circleShapeNew ballBody radius (Vect 0 0)+ shapeFriction ballShape $= 0.9+ shapeElasticity ballShape $= 1+ spaceAddShape space ballShape++ anotherBall <- bodyNew mass100 moment100+ spaceAddBody space anotherBall++ anotherBallShape <- circleShapeNew anotherBall radius (Vect 0 0)+ shapeFriction anotherBallShape $= 0.9+ shapeElasticity anotherBallShape $= 0.4+ spaceAddShape space anotherBallShape++ putMVar dm+ [ mkStaticObj $ Segment segA segB+ , mkBallBody ballBody radius+ , mkBallBody anotherBall radius+ ]++ void $ forever $ do+ bodyPosition ballBody $= Vect (-15) 30+ bodyPosition anotherBall $= Vect (-5) 75+ -- need to reset ball velocity after previous iteration+ bodyVelocity ballBody $= Vect 0 0+ bodyAngularVelocity ballBody $= 0+ bodyVelocity anotherBall $= Vect 0 0+ bodyAngularVelocity anotherBall $= 0++ -- Now that it's all set up, we simulate all the objects in the space by+ -- stepping forward through time in small increments called steps.+ -- It is *highly* recommended to use a fixed size time step.+ let timeStep = 1/60+ runFor 3 timeStep $ \time -> do+ pos <- get $ bodyPosition ballBody+ vel <- get $ bodyVelocity ballBody+ printf "Time is %4.2f. ballBody is at (%6.2f, %6.2f), it's velocity is (%6.2f, %6.2f).\n"+ time (vX pos) (vY pos) (vX vel) (vY vel)++ threadDelay $ round $ timeStep * 1000 * 1000+ spaceStep space timeStep++ shapeFree ballShape+ bodyFree ballBody+ shapeFree ground+ spaceFree space+ where+ runFor time step inner = go time+ where+ go time'+ | time' <= 0 = pure ()+ | otherwise = inner (time - time') *> go (time' - step)++display :: MVar [VisObj] -> IO ()+display dm = do+ d <- takeMVar dm+ N.run 800 600 "Chiphunk" $+ N.showFPS "Liberation Sans" $+ N.loadFont "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf" "Liberation Sans" $+ N.asWindow $+ N.translateP 400 300 .+ N.scaleP' (0, 0) 10 .+ N.scalePy (0, 0) (-1) .+ N.pictures <$>+ sequence ((render <$>) . runVisObj <$> d)+ where+ render = \case+ Segment (Vect ax ay) (Vect bx by) -> N.stroke (NVG.Color 1 1 1 1) $+ N.line (realToFrac ax, realToFrac ay) (realToFrac bx, realToFrac by)+ Ball (Vect x y) r a ->+ let c = (realToFrac x, realToFrac y)+ in N.stroke (NVG.Color 1 1 1 1) $+ N.rotateS c (realToFrac a) $+ N.shapes+ [ N.circle c (realToFrac r)+ , N.line c (realToFrac $ x - r / 2, realToFrac y)+ ]++data VisShape =+ Segment+ { segEndpointA :: Vect+ , segEndpointB :: Vect+ }+ | Ball+ { ballCenter :: Vect+ , ballRadius :: Double+ , ballAngle :: Double+ }+ deriving Show++newtype VisObj = VisObj+ { runVisObj :: IO VisShape+ }++mkRefObj :: IORef VisShape -> VisObj+mkRefObj r = VisObj $ readIORef r++mkStaticObj :: VisShape -> VisObj+mkStaticObj = VisObj . pure++mkBallBody :: Body -> Double -> VisObj+mkBallBody b r = VisObj $ Ball <$> get (bodyPosition b)+ <*> pure r+ <*> get (bodyAngle b)
+ chiphunk.cabal view
@@ -0,0 +1,137 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.30.0.+--+-- see: https://github.com/sol/hpack+--+-- hash: 6654f10bfcf93a216508e1f3fa7c6a251033427487355ade61c5dea7ad87e255++name: chiphunk+version: 0.1.0.0+synopsis: Haskell bindings for Chipmunk2D physics engine+description: Please see the README on GitHub at <https://github.com/CthulhuDen/chiphunk#readme>+category: Physics+homepage: https://github.com/CthulhuDen/chiphunk#readme+author: Cthulhu+maintainer: cthulhu.den@gmail.com+copyright: Cthulhu (c) 2018+license: BSD3+license-file: LICENSE+build-type: Simple+extra-source-files:+ README.md+ Chipmunk2D-7.0.2/include/chipmunk/chipmunk_ffi.h+ Chipmunk2D-7.0.2/include/chipmunk/chipmunk.h+ Chipmunk2D-7.0.2/include/chipmunk/chipmunk_private.h+ Chipmunk2D-7.0.2/include/chipmunk/chipmunk_structs.h+ Chipmunk2D-7.0.2/include/chipmunk/chipmunk_types.h+ Chipmunk2D-7.0.2/include/chipmunk/chipmunk_unsafe.h+ Chipmunk2D-7.0.2/include/chipmunk/cpArbiter.h+ Chipmunk2D-7.0.2/include/chipmunk/cpBB.h+ Chipmunk2D-7.0.2/include/chipmunk/cpBody.h+ Chipmunk2D-7.0.2/include/chipmunk/cpConstraint.h+ Chipmunk2D-7.0.2/include/chipmunk/cpDampedRotarySpring.h+ Chipmunk2D-7.0.2/include/chipmunk/cpDampedSpring.h+ Chipmunk2D-7.0.2/include/chipmunk/cpGearJoint.h+ Chipmunk2D-7.0.2/include/chipmunk/cpGrooveJoint.h+ Chipmunk2D-7.0.2/include/chipmunk/cpHastySpace.h+ Chipmunk2D-7.0.2/include/chipmunk/cpMarch.h+ Chipmunk2D-7.0.2/include/chipmunk/cpPinJoint.h+ Chipmunk2D-7.0.2/include/chipmunk/cpPivotJoint.h+ Chipmunk2D-7.0.2/include/chipmunk/cpPolyline.h+ Chipmunk2D-7.0.2/include/chipmunk/cpPolyShape.h+ Chipmunk2D-7.0.2/include/chipmunk/cpRatchetJoint.h+ Chipmunk2D-7.0.2/include/chipmunk/cpRobust.h+ Chipmunk2D-7.0.2/include/chipmunk/cpRotaryLimitJoint.h+ Chipmunk2D-7.0.2/include/chipmunk/cpShape.h+ Chipmunk2D-7.0.2/include/chipmunk/cpSimpleMotor.h+ Chipmunk2D-7.0.2/include/chipmunk/cpSlideJoint.h+ Chipmunk2D-7.0.2/include/chipmunk/cpSpace.h+ Chipmunk2D-7.0.2/include/chipmunk/cpSpatialIndex.h+ Chipmunk2D-7.0.2/include/chipmunk/cpTransform.h+ Chipmunk2D-7.0.2/include/chipmunk/cpVect.h++flag library-only+ manual: False+ default: True++library+ exposed-modules:+ Chiphunk.Low+ other-modules:+ Chiphunk.Low.Math+ Chiphunk.Low.Types+ Chiphunk.Low.Internal+ Chiphunk.Low.Helper+ Chiphunk.Low.Vect+ Chiphunk.Low.BB+ Chiphunk.Low.Body+ Chiphunk.Low.Shape+ Chiphunk.Low.Space+ Chiphunk.Low.Constraint+ Chiphunk.Low.Callback+ Chiphunk.Low.Arbiter+ hs-source-dirs:+ src+ ghc-options: -Wall+ include-dirs:+ Chipmunk2D-7.0.2/include+ src/Chiphunk+ c-sources:+ Chipmunk2D-7.0.2/src/chipmunk.c+ Chipmunk2D-7.0.2/src/cpConstraint.c+ Chipmunk2D-7.0.2/src/cpSpaceStep.c+ Chipmunk2D-7.0.2/src/cpShape.c+ Chipmunk2D-7.0.2/src/cpSpace.c+ Chipmunk2D-7.0.2/src/cpArbiter.c+ Chipmunk2D-7.0.2/src/cpArray.c+ Chipmunk2D-7.0.2/src/cpBBTree.c+ Chipmunk2D-7.0.2/src/cpBody.c+ Chipmunk2D-7.0.2/src/cpCollision.c+ Chipmunk2D-7.0.2/src/cpDampedRotarySpring.c+ Chipmunk2D-7.0.2/src/cpDampedSpring.c+ Chipmunk2D-7.0.2/src/cpGearJoint.c+ Chipmunk2D-7.0.2/src/cpGrooveJoint.c+ Chipmunk2D-7.0.2/src/cpHashSet.c+ Chipmunk2D-7.0.2/src/cpHastySpace.c+ Chipmunk2D-7.0.2/src/cpMarch.c+ Chipmunk2D-7.0.2/src/cpPinJoint.c+ Chipmunk2D-7.0.2/src/cpPivotJoint.c+ Chipmunk2D-7.0.2/src/cpPolyline.c+ Chipmunk2D-7.0.2/src/cpPolyShape.c+ Chipmunk2D-7.0.2/src/cpRatchetJoint.c+ Chipmunk2D-7.0.2/src/cpRobust.c+ Chipmunk2D-7.0.2/src/cpRotaryLimitJoint.c+ Chipmunk2D-7.0.2/src/cpSimpleMotor.c+ Chipmunk2D-7.0.2/src/cpSlideJoint.c+ Chipmunk2D-7.0.2/src/cpSpaceComponent.c+ Chipmunk2D-7.0.2/src/cpSpaceDebug.c+ Chipmunk2D-7.0.2/src/cpSpaceHash.c+ Chipmunk2D-7.0.2/src/cpSpaceQuery.c+ Chipmunk2D-7.0.2/src/cpSpatialIndex.c+ Chipmunk2D-7.0.2/src/cpSweep1D.c+ + src/Chiphunk/wrapper.c+ build-depends:+ StateVar >=1.1.1.1 && <1.2+ , base >=4.7 && <5+ , safe-exceptions >=0.1.7.0 && <0.2+ , vector-space >=0.13 && <0.16+ default-language: Haskell2010++executable chiphunk+ main-is: Main.hs+ other-modules:+ Paths_chiphunk+ hs-source-dirs:+ app+ ghc-options: -Wall -O2 -threaded -rtsopts -with-rtsopts=-N2+ build-depends:+ async >=2.2.1 && <2.3+ , base >=4.7 && <5+ , chiphunk+ , nanovg >=0.6.0.0 && <0.7+ , nanovg-simple >=0.4.0.0 && <0.5+ if flag(library-only)+ buildable: False+ default-language: Haskell2010
+ src/Chiphunk/Low.hs view
@@ -0,0 +1,998 @@+-- | Description: Low-level Haskell bindings to Chipmunk2D physics library+-- Chiphunk is a __low-level__ Haskell bindings for the <https://chipmunk-physics.net/ Chipmunk2D physics engine>.+-- It includes most (almost all) of the functions mentioned in the main documentation for Chipmunk2D,+-- except for some (relatively) exotic ones, which may be added later per request.+module Chiphunk.Low+ ( -- * Disclaymer++ -- | This bindings are so low-level so that they even require you to free the memory the Chipmunk2D has allocated+ -- for your objects. Module with more high-level api can be built around this low-level bingings at some point,+ -- in the meantime, however, you're advised to provide wrapper layer for your games so that you do not have to+ -- keep track of such things in the core of your game's logic.+ --+ -- See below for an adoptation of the original Chipmunk documentation available+ -- <https://chipmunk-physics.net/release/ChipmunkLatest-Docs/ here>. I've skipped some sections not related+ -- to the bindings, like the reason author chose C language in the first place and limitations of the C api.+ -- Obviously, it's thanks to that choice that I was able to write bindings around the library in Haskell.+ --+ -- __Howling Moon Software is not affiliated with this bindings.__+ -- __In all of the following \"I\" refers to the original documentation author for Chipmunk2D.__++ -- * Chipmunk2D 7.0.2++ -- | Chipmunk2D is a 2D rigid body physics library distributed under the MIT license.+ -- It is blazingly fast, portable, numerically stable, and easy to use.+ -- For this reason it has been used in hundreds of games across just about every system you can name.+ -- This includes top quality titles such as Night Sky for the Wii and many #1 sellers on the iPhone App Store!+ -- I’ve put thousands of hours of work over many years to make Chipmunk2D what it is today.+ -- If you find Chipmunk2D has saved you a lot of time, please consider+ -- <https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6666552 donating>.+ -- You’ll make an indie game developer very happy!++ -- | First of all, I would like to give a Erin Catto a big thank you, as Chipmunk2D’s impulse solver+ -- was directly inspired by his example code way back in 2006.+ -- (Now a full fledged physics engine all its own: <http://www.box2d.org/ Box2D.org>).+ -- His contact persistence idea allows for stable stacks of objects with very few iterations of the solver.+ -- My previous solver produced mushy piles of objects or required a large amount of CPU to operate stably.++ -- ** Support++ -- | The best way to get support is to visit the <http://www.slembcke.net/forums/viewforum.php?f=1 Chipmunk Forums>.+ -- There are plenty of people around using Chipmunk on the just about every platform+ -- I’ve ever heard of. If you are working on a commercial project, Howling Moon Software+ -- (my company) is <http://howlingmoonsoftware.com/contracting.php available for contracting>.+ -- We can help with implementing custom Chipmunk behaviors,+ -- as well as priority bug fixes and performance tuning.++ -- ** Contact++ -- | If you find any bugs in Chipmunk, errors or broken links in this document,+ -- or have a question or comment about Chipmunk you can contact me at+ -- slembcke(at)gmail(dot)com. (email or GTalk)++ -- ** License++ -- | Chipmunk is licensed under the MIT license.+ --+ -- @+ -- Copyright (c) 2007-2015 Scott Lembcke and Howling Moon Software+ --+ -- Permission is hereby granted, free of charge, to any person obtaining a copy+ -- of this software and associated documentation files (the "Software"), to deal+ -- in the Software without restriction, including without limitation the rights+ -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ -- copies of the Software, and to permit persons to whom the Software is+ -- furnished to do so, subject to the following conditions:+ --+ -- The above copyright notice and this permission notice shall be included in+ -- all copies or substantial portions of the Software.+ --+ -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ -- SOFTWARE.+ -- @+ --+ -- This means that you do not have to buy a license or pay to use Chipmunk in commercial projects. (Though we really appreciate donations)++ -- ** Links++ -- |+ -- * <http://chipmunk2d.net/forum Chipmunk Forums> – The official forum Chipmunk2D forum.+ --+ -- * <http://howlingmoonsoftware.com/ Howling Moon Software> – The software company I co-founded.+ -- (We are available for contract work!)+ --+ -- * <http://chipmunk2d.net/games.php Games> – A small list of games we know that use Chipmunk.++ -- * Hello Chipmunk (World)++ -- | Hello world Chipmunk style. Create a simple simulation where a ball falls onto a static line segment,+ -- then rolls off. Print out the coordinates of the ball.+ --+ -- @+ -- main :: IO ()+ -- main = do+ -- let gravity = 'Vect' 0 (-100)+ --+ -- -- Create an empty space.+ -- space <- 'spaceNew'+ -- 'spaceGravity' space $= gravity+ --+ -- -- Add a static line segment shape for the ground.+ -- -- We'll make it slightly tilted so the ball will roll off.+ -- -- We attach it to a static body to tell Chipmunk it shouldn't be movable.+ -- static <- get $ 'spaceStaticBody' space+ -- ground <- 'segmentShapeNew' static (Vect (-20) 5) (Vect 20 (-5)) 0+ -- 'shapeFriction' ground $= 1+ -- 'spaceAddShape' space ground+ --+ -- -- Now let's make a ball that falls onto the line and rolls off.+ -- -- First we need to make a cpBody to hold the physical properties of the object.+ -- -- These include the mass, position, velocity, angle, etc. of the object.+ -- -- Then we attach collision shapes to the 'Body' to give it a size and shape.+ --+ -- let radius = 5+ -- let mass = 1+ --+ -- -- The moment of inertia is like mass for rotation+ -- -- Use the momentFor* functions to help you approximate it.+ -- let moment = 'momentForCircle' mass 0 radius (Vect 0 0)+ --+ -- -- The spaceAdd* functions return the thing that you are adding.+ -- ballBody <- 'bodyNew' mass moment+ -- 'spaceAddBody' space ballBody+ -- 'bodyPosition' ballBody $= Vect 0 15+ --+ -- -- Now we create the collision shape for the ball.+ -- -- You can create multiple collision shapes that point to the same body.+ -- -- They will all be attached to the body and move around to follow it.+ -- ballShape <- 'circleShapeNew' ballBody radius (Vect 0 0)+ -- 'spaceAddShape' space ballShape+ -- 'shapeFriction' ballShape $= 0.7+ --+ -- -- Now that it's all set up, we simulate all the objects in the space by+ -- -- stepping forward through time in small increments called steps.+ -- -- It is *highly* recommended to use a fixed size time step.+ -- let timeStep = 1/60+ -- runFor 2 timeStep $ \time -> do+ -- pos <- get $ 'bodyPosition' ballBody+ -- vel <- get $ 'bodyVelocity' ballBody+ -- printf "Time is %4.2f. ballBody is at (%6.2f, %6.2f), it's velocity is (%6.2f, %6.2f).\n"+ -- time (vX pos) (vY pos) (vX vel) (vY vel)+ --+ -- 'spaceStep' space timeStep+ --+ -- 'shapeFree' ballShape+ -- 'bodyFree' ballBody+ -- 'shapeFree' ground+ -- 'spaceFree' space+ -- where+ -- runFor time step inner = go time+ -- where+ -- go time'+ -- | time' <= 0 = pure ()+ -- | otherwise = inner (time - time') *> go (time' - step)+ -- @++ -- * Chipmunk2D Basics++ -- ** Overview++ -- | There are 4 basic object types you will use in Chipmunk.++ -- |+ -- * __Rigid Bodies__ ('Body'): A rigid body holds the physical properties of an object.+ -- (mass, position, rotation, velocity, etc.) It does not have a shape until you attach one or more collision shapes+ -- to it. If you’ve done physics with particles before, rigid bodies differ in that they are able to rotate.+ -- Rigid bodies generally tend to have a 1:1 correlation to sprites in a game.+ -- You should structure your game so that you use the position and rotation of the rigid body+ -- for drawing your sprite.+ --+ -- * __Collision Shapes__ ('Shape'): By attaching shapes to bodies, you can define the a body’s shape.+ -- You can attach as many shapes to a single body as you need to in order to define a complex shape.+ -- Shapes contain the surface properties of an object such as how much friction or elasticity it has.+ --+ -- * __Constraints/Joints__ ('Constraint'): Constraints and joints describe how bodies are attached to each other.+ --+ -- * __Spaces__ ('Space'): Spaces are containers for simulating objects in Chipmunk.+ -- You add bodies, shapes and joints to a space and then update the space as a whole.+ -- They control how all the rigid bodies, shapes, and constraints interact together.+ --+ -- There is often confusion between rigid bodies and their collision shapes in Chipmunk+ -- and how they relate to sprites. A sprite would be a visual representation of an object,+ -- while a collision shape is an invisible property that defines how objects should collide.+ -- Both the sprite’s and the collision shape’s position and rotation are controlled by the motion of a rigid body.+ -- Generally you want to create a game object type that ties these things all together.++ -- ** Memory Management the Chipmunk way++ -- | For most of the structures you will use, Chipmunk uses a more or less standard and straightforward set+ -- of memory management functions. Take the 'Space' struct for example:+ --+ -- * 'spaceNew' — Allocates and initializes a 'Space' struct.+ --+ -- * 'spaceFree' — Destroys and frees the 'Space' struct.+ --+ -- You are responsible for freeing any structs that you allocate. Chipmunk does not do reference counting or garbage collection. If you call a new function, you must call the matching free function or you will leak memory.++ -- ** Math the Chipmunk way++ -- | First of all, Chipmunk uses double precision floating point numbers throughout its calculations by default.+ -- This is likely to be faster on most modern desktop processors,+ -- and means you don’t have to worry as much about floating point accuracy.+ --+ -- However, there are a few unique functions you will probably find very useful:+ fClamp+ , fLerp+ , fLerpConst++ -- * Chipmunk Vectors++ -- ** Struct Definition, Constants and Constructors+ , Vect (..)+ , vZero+ , cpv++ -- ** Operations++ -- | (__Note for bindings__: Most of these are Chipmunk2D-style aliases for 'Vect' typeclasses methods:+ -- 'Eq', 'Data.VectorSpace.AdditiveGroup', 'Data.VectorSpace.VectorSpace', 'Data.VectorSpace.InnerSpace',+ -- 'Data.Cross.HasCross2')+ , vEql+ , vAdd+ , vSub+ , vNeg+ , vMult+ , vDot+ , vCross+ , vPerp+ , vRPerp+ , vProject+ , vRotate+ , vUnRotate+ , vLength+ , vLengthSq+ , vLerp+ , vLerpConst+ , vSLerp+ , vSLerpConst+ , vNormalize+ , vClamp+ , vDist+ , vDistSq+ , vNear+ , vForAngle+ , vToAngle++ -- * Chipmunk Axis Aligned Bounding Boxes++ -- ** Struct Definition and Constructors++ , BB (..)+ , bbNew+ , bbNewForExtents+ , bbNewForCircle++ -- ** Operations++ , bbIntersects+ , bbContainsBB+ , bbContainsVect+ , bbMerge+ , bbExpand+ , bbCenter+ , bbArea+ , bbMergedArea+ , bbSegmentQuery+ , bbIntersectsSegment+ , bbClampVect+ , bbWrapVect++ -- * Chipmunk Rigid Bodies+ , Body++ -- ** Dynamic, Kinematic, and Static Bodies+ , BodyType (..)++ -- ** Movement, Teleportation, and Velocity++ -- | A graphics engine only needs to know the position of an object for each frame that its drawn.+ -- For a physics engine, this isn’t enough information to calculate a collision response.+ -- When you set the position of a body, you are effectively asking it to teleport itself.+ -- This means that it will instantly move to its new position instead of moving through space and time+ -- like a normal object. If you teleport an object so that it overlaps another one,+ -- the best the physics engine can do is to attempt to push the objects apart again+ -- since there is no information about their movement. This generally results in very mushy looking collisions.+ -- So instead of setting the position of an object, it’s better to set its velocity and allow the physics engine+ -- to update the position. That way it can resolve any resulting colisions natuarally since it knows+ -- how the objects were moving. This is why kinematic bodies work the way they do.+ -- You set the velocity, and the physics updates their position so the two are never out of sync.+ --+ -- For dynamic bodies, setting the velocity explicitly every frame can cause problems.+ -- For example, a problem occurs when a light dynamic body (like a person) is pressed against a heavy dynamic body+ -- (like a car), and you set velocity of the small object so that it’s pushing it into the big body.+ -- To the physics engine, the change in velocity is the same as applying a large impulse+ -- (a very short, very large force). Even if the velocity is low, the large force can allow the small body+ -- to push the big body, even when it normally wouldn’t be able to. For example, a person walking into a car+ -- can overpower the car’s friction and cause it to creep along the ground slowly.+ -- Additionally, when you set the velocity of an object that is already in contact,+ -- it can cause the two objects to overlap by a small amount. The easiest way to avoid both of these problems+ -- is to make smaller changes to the body’s velocity, accelerating it over a fraction of a second+ -- instead of a single frame. An even better solution, which is covered more thoroughly later,+ -- is to use constraints to move the object.++ -- ** Memory Management Functions++ -- | Standard set of Chipmunk memory management functions.+ , bodyNew+ , bodyNewKinematic+ , bodyNewStatic+ , bodyFree++ -- ** Creating Dynamic Bodies++ -- | There are two ways to set up a dynamic body. The easiest option is to create a body+ -- with a mass and moment of 0, and set the mass or density of each collision shape added to the body.+ -- Chipmunk will automatically calculate the mass, moment of inertia, and center of gravity for you.+ -- This is probably preferred in most cases.+ --+ -- The other option is to set the mass of the body when it’s created, and leave the mass of the shapes+ -- added to it as 0.0. This approach is more flexible, but is not as easy to use.+ -- __Don’t__ set the mass of both the body and the shapes. If you do so,+ -- it will recalculate and overwite your custom mass value when the shapes are added to the body.++ -- ** Properties++ -- | Chipmunk provides getter/setter functions for a number of properties on rigid bodies.+ -- Setting most properties automatically wakes the rigid bodies up if they were sleeping.++ , bodyType+ , bodyMass+ , bodyMoment+ , bodyPosition+ , bodyCenterOfGravity+ , bodyVelocity+ , bodyForce+ , bodyAngle+ , bodyAngularVelocity+ , bodyTorque+ , bodyRotation+ , bodySpace+ , bodyUserData++ -- ** Moment of Inertia and Area Helper Functions++ -- | Use the following functions to approximate the moment of inertia for your body,+ -- adding the results together if you want to use more than one.+ , momentForCircle+ , momentForSegment+ , momentForPoly+ , momentForBox++ -- | Use the following functions to get the area for common Chipmunk shapes if you want to approximate masses+ -- or density or whatnot.+ , areaForCircle+ , areaForSegment+ , areaForPoly++ -- ** Coordinate Conversion Functions++ -- | Many things are defined in coordinates local to a body meaning that the (0,0) is at the center of gravity+ -- of the body and the axis rotate along with the body.+ , bodyLocalToWorld+ , bodyWorldToLocal++ -- ** Velocity Conversion Functions++ -- | It’s often useful to know the absolute velocity of a point on the surface of a body+ -- since the angular velocity affects everything except the center of gravity.+ , bodyVelocityAtWorldPoint++ -- ** Applying Forces and Torques++ -- | People are sometimes confused by the difference between a force and an impulse.+ -- An impulse is a very large force applied over a very short period of time.+ -- Some examples are a ball hitting a wall or cannon firing. Chipmunk treats impulses as if they occur+ -- instantaneously by adding directly to the velocity of an object.+ -- Both impulses and forces are affected the mass of an object.+ -- Doubling the mass of the object will halve the effect.+ , bodyApplyForceAtWorldPoint+ , bodyApplyForceAtLocalPoint+ , bodyApplyImpulseAtWorldPoint+ , bodyApplyImpulseAtLocalPoint++ -- ** Sleeping Functions++ -- | Chipmunk supports a sleeping feature which improves performance by not simulating groups of objects+ -- that aren’t moving. Read more about it in the 'Space' section.+ , bodyIsSleeping+ , bodyActivate+ , bodySleep+ , bodyActivateStatic+ , bodySleepWithGroup++ -- ** Iterators+ , BodyShapeIteratorFunc+ , bodyEachShape+ , BodyConstraintIteratorFunc+ , bodyEachConstraint+ , BodyArbiterIteratorFunc+ , bodyEachArbiter++ -- * Chipmunk Collision Shapes++ , Shape++ -- ** Properties++ -- | Chipmunk provides getter/setter functions for a number of properties on collision shapes.+ -- Setting most properties will automatically wake the attached rigid body, if it’s sleeping.++ , shapeBody+ , shapeBB+ , shapeSensor+ , shapeElasticity+ , shapeFriction+ , shapeSurfaceVelocity+ , shapeCollisionType+ , ShapeFilter (..)+ , shapeFilter+ , shapeSpace+ , shapeUserData++ -- ** Fast Collision Filtering using ShapeFilter++ -- | Chipmunk has two primary means of ignoring collisions: groups and category masks.+ --+ -- __Groups__ are used to ignore collisions between parts on a complex object.+ -- A ragdoll is a good example. When jointing an arm onto the torso, you’ll want them to allow them to overlap.+ -- Groups allow you to do exactly that. Shapes that have the same group don’t generate collisions.+ -- So by placing all of the shapes in a ragdoll in the same group, you’ll prevent it from colliding+ -- against other parts of itself.+ -- __Category__ masks allow you to mark which categories an object belongs to+ -- and which categories it collidies with.+ --+ -- For example, a game has four collision categories: player (0), enemy (1), player bullet (2),+ -- and enemy bullet (3). Neither players nor enemies should not collide with their own bullets,+ -- and bullets should not collide with other bullets.+ -- However, players collide with enemy bullets, and enemies collide with player bullets.+ --+ -- +-----------------+-----------------+---------------++ -- | Object | Object Category | Category Mask |+ -- +=================+=================+===============++ -- | \"Player\" | 1 | 4, 5 |+ -- +-----------------+-----------------+---------------++ -- | \"Enemy\" | 2 | 2, 3, 5 |+ -- +-----------------+-----------------+---------------++ -- | "Player Bullet" | 3 | 1, 5 |+ -- +-----------------+-----------------+---------------++ -- | "Enemy Bullet" | 4 | 2, 5 |+ -- +-----------------+-----------------+---------------++ -- | \"Walls\" | 5 | 1, 2, 3, 4 |+ -- +-----------------+-----------------+---------------++ --+ -- Note that everything in this example collides with walls. Additionally, the enemies collide with eachother.+ --+ -- By default, objects exist in every category and collide with every category.+ --+ -- Objects can fall into multiple categories. For instance, you might have a category for a red team,+ -- and have a red player bullet. In the above example, each object only has one category.+ --+ -- There is one last way of filtering collisions using collision handlers. See the section on callbacks+ -- for more information. Collision handlers can be more flexible, but can be slower.+ -- Fast collision filtering rejects collisions before running the expensive collision detection code,+ -- so using groups or category masks is preferred.++ -- ** Memory Management Functions+ , shapeFree++ -- ** Misc functions+ , shapeCacheBB+ , shapeUpdate++ -- ** Working With Circle Shapes+ , circleShapeNew++ -- ** Working With Segment Shapes+ , segmentShapeNew+ , segmentShapeNeighbors++ -- ** Working With Polygon Shapes+ , polyShapeNew+ , polyShapeNewRaw++ -- *** Boxes++ -- | Because boxes are so common in physics games, Chipmunk provides shortcuts to create box shaped polygons.+ -- The boxes will always be centered at the center of gravity of the body you are attaching them to.+ -- Adding a small radius will bevel the corners and can significantly reduce problems+ -- where the box gets stuck on seams in your geometry. If you want to create an off-center box,+ -- you will need to use 'polyShapeNew'.+ , boxShapeNew+ , boxShapeNew2++ -- *** Poly Shape Helper Functions+ , centroidForPoly++ -- *** Convex Hull Helper Functions+ , convexHull++ -- ** Modifying 'Shape's++ -- | The short answer is that you can’t because the changes would be only picked up as a change to the position+ -- of the shape’s surface, but not its velocity.++ -- ** Notes++ -- |+ -- * You can attach multiple collision shapes to a rigid body. This should allow you to create almost any shape+ -- you could possibly need.+ --+ -- * Shapes attached to the same rigid body will never generate collisions. You don’t have to worry+ -- about overlap when attaching multiple shapes to a rigid body.+ --+ -- * Make sure you add both the body and its collision shapes to a space.++ -- * Chipmunk Spaces+ , Space++ -- ** What Are Iterations, and Why Should I Care?++ -- | Chipmunk uses an iterative solver to figure out the forces between objects in the space.+ -- What this means is that it builds a big list of all of the collisions, joints, and other constraints+ -- between the bodies and makes several passes over the list considering each one individually.+ -- The number of passes it makes is the iteration count, and each iteration makes the solution more accurate.+ -- If you use too many iterations, the physics should look nice and solid, but may use up too much CPU time.+ -- If you use too few iterations, the simulation may seem mushy or bouncy when the objects should be solid.+ -- Setting the number of iterations lets you balance between CPU usage and the accuracy of the physics.+ -- Chipmunk’s default of 10 iterations is sufficient for most simple games.++ -- ** Sleeping++ -- | Spaces can disable entire groups of objects that have stopped moving to save CPU time and battery life.+ -- In order to use this feature you must do two things. You must enable sleeping explicitly+ -- by choosing a time threshold value with 'spaceSetSleepTimeThreshold'. This threshold is the amount of time+ -- something must be idle before it falls asleep. 'spaceSetIdleSpeedThreshold' defines what is considered idle.+ -- If you do not set idle speed threshold explicitly, a value will be chosen automatically+ -- based on the current amount of gravity. Be mindful that objects cannot fall asleep if they are touching+ -- or jointed to a kinematic body.++ -- ** Properties++ , spaceIterations+ , spaceGravity+ , spaceDamping+ , spaceIdleSpeedThreshold+ , spaceSleepTimeThreshold+ , spaceCollisionSlop+ , spaceCollisionBias+ , spaceCollisionPersistence+ , spaceCurrentTimeStep+ , spaceIsLocked+ , spaceUserData+ , spaceStaticBody++ -- ** Memory Management Functions++ -- | More standard Chipmunk memory functions.+ , spaceNew+ , spaceFree++ -- ** Operations++ -- | These functions add and remove shapes, bodies and constraints from space. The add/remove functions+ -- cannot be called from within a callback other than a 'postStep' callback (which is different than a 'postSolve'+ -- callback!). Attempting to add or remove objects from the space while 'spaceStep' is still executing+ -- will throw an assertion. See the callbacks section for more information. Be careful not to free bodies+ -- before removing shapes and constraints attached to them or you will cause crashes..+ -- The contains functions allow you to check if an object has been added to the space or not.+ , spaceAddShape+ , spaceAddBody+ , spaceAddConstraint+ , spaceRemoveShape+ , spaceRemoveBody+ , spaceRemoveConstraint+ , spaceContainsShape+ , spaceContainsBody+ , spaceContainsConstraint++ -- ** Spatial Indexing++ -- | Occasionally, you might want to update the collision detection data for a shape.+ -- If you move a static shape or a static body you must do this to let Chipmunk know+ -- it needs to have its collision detection data updated. You may also want to manually update the collision data+ -- for normal shapes if you move them and still want to perform queries against them+ -- before the next call to 'spaceStep'.+ , spaceReindexShape+ , spaceReindexShapesForBody+ , spaceReindexStatic++ -- ** Iterators+ , SpaceBodyIteratorFunc+ , spaceEachBody+ , SpaceShapeIteratorFunc+ , spaceEachShape+ , SpaceConstraintIteratorFunc+ , spaceEachConstraint++ -- ** Simulating the Space+ , spaceStep++ -- * Notes++ -- |+ -- * When removing objects from the space, make sure you remove any other objects that reference it.+ -- For instance, when you remove a body, remove the joints and shapes attached to it.+ --+ -- * Using more iterations or smaller time steps will increase the physics quality, but also increase the CPU usage.++ -- * Chipmunk Constraints+ , Constraint++ -- ** What constraints are and what they are not++ -- | Constraints in Chipmunk are all velocity based constraints.+ -- This means that they act primarily by synchronizing the velocity of two bodies.+ -- A pivot joint holds two anchor points on two separate bodies together by defining equations that say+ -- that the velocity of the anchor points must be the same and calculating impulses to apply to the bodies+ -- to try and keep it that way. A constraint takes a velocity as it’s primary input and produces a velocity change+ -- as its output. Some constraints, (joints in particular) apply velocity changes to correct differences+ -- in positions. More about this in the next section.+ --+ -- A spring connected between two bodies is not a constraint. It’s very constraint-like as it creates forces+ -- that affect the velocities of the two bodies, but a spring takes distances as input and produces forces+ -- as its output. If a spring is not a constraint, then why do I have two varieties of spring constraints you ask?+ -- The reason is because they are damped springs. The damping associated with the spring is a true constraint+ -- that creates velocity changes based on the relative velocities of the two bodies it links.+ -- As it is convenient to put a damper and a spring together most of the time, I figured I might as well just apply+ -- the spring force as part of the constraint instead of having a damper constraint and having the user+ -- calculate and apply their own spring forces separately.++ -- ** Properties++ , constraintBodyA+ , constraintBodyB+ , constraintMaxForce+ , constraintErrorBias+ , constraintMaxBias+ , constraintSpace+ , constraintCollideBodies+ , constraintUserData+ , constraintImpulse++ -- ** Error correction by Feedback++ -- | Joints in Chipmunk are not perfect. A pin joint can’t maintain the exact correct distance between its anchor+ -- points, nor can a pivot joint hold its anchor points completely together. Instead, they are designed+ -- to deal with this by correcting themselves over time. Since Chipmunk 5, you have a fair amount of extra control+ -- over how joints correct themselves and can even use this ability to create physical effects+ -- that allow you to use joints in unique ways:+ --+ -- * Servo motors – Ex: open/close doors or rotate things without going over a maximum force.+ --+ -- * Winches – Pull one object towards another at a constant speed without going over a maximum force.+ --+ -- * Mouse manipulation – Interact with objects smoothly given coarse/shaky mouse input.+ --+ -- There are three properties of 'Constraint' structs that control the error correction,+ -- maxForce, maxBias, and biasCoef. maxForce is pretty self explanatory, a joint or constraint+ -- will not be able to use more than this amount of force in order to function. If it needs more force+ -- to be able to hold itself together, it will fall apart. maxBias is the maximum speed at which error correction+ -- can be applied. If you change a property on a joint so that the joint will have to correct itself,+ -- it normally does so very quickly. By setting a maxSpeed you can make the joint work like a servo,+ -- correcting itself at a constant rate over a longer period of time. Lastly, biasCoef is the percentage+ -- of error corrected every step before clamping to a maximum speed. You can use this+ -- to make joints correct themselves smoothly instead of at a constant speed, but is probably the least useful+ -- of the three properties by far.++ -- ** Constraints and Collision Shapes++ -- | Neither constraints or collision shapes have any knowledge of the other.+ -- When connecting joints to a body the anchor points don’t need to be inside of any shapes attached to the body+ -- and it often makes sense that they shouldn’t. Also, adding a constraint between two bodies+ -- doesn’t prevent their collision shapes from colliding. In fact, this is the primary reason+ -- that the collision group property exists.++ -- ** Video Tour of Current Joint Types++ -- | http://www.youtube.com/watch?v=ZgJJZTS0aMM++ -- ** Shared Memory Management Functions+ , constraintFree++ -- ** Constraint Types++ -- *** Pin Joints+ , pinJointNew++ -- **** Properties+ , pinJointAnchorA+ , pinJointAnchorB+ , pinJointDist++ -- *** Slide Joints+ , slideJointNew++ -- **** Properties+ , slideJointAnchorA+ , slideJointAnchorB+ , slideJointMin+ , slideJointMax++ -- *** Pivot Joints++ -- | (__Note for bindings__: So each instance of pivot joint can be replaced with pin joint with dist of 0?)++ , pivotJointNew+ , pivotJointNew2++ -- **** Properties+ , pivotJointAnchorA+ , pivotJointAnchorB++ -- *** Groove Joint+ , grooveJointNew++ -- **** Properties+ , grooveJointGrooveA+ , grooveJointGrooveB+ , grooveJointAnchorB++ -- *** Damped Spring+ , dampedSpringNew++ -- **** Properties+ , dampedSpringAnchorA+ , dampedSpringAnchorB+ , dampedSpringRestLength+ , dampedSpringStiffness+ , dampedSpringDamping++ -- *** Damped Rotary Spring++ -- | Like a damped spring, but works in an angular fashion.+ , dampedRotarySpringNew++ -- **** Properties+ , dampedRotarySpringRestAngle+ , dampedRotarySpringStiffness+ , dampedRotarySpringDamping++ -- *** Rotary Limit Joint++ -- | Constrains the relative rotations of two bodies.+ -- It is implemented so that it’s possible to for the range to be greater than a full revolution.+ , rotaryLimitJointNew++ -- **** Properties+ , rotaryLimitJointMin+ , rotaryLimitJointMax++ -- *** Ratchet Joint++ -- | Works like a socket wrench.+ , ratchetJointNew++ -- **** Properties+ , ratchetJointAngle+ , ratchetJointPhase+ , ratchetJointRatchet++ -- *** Gear Joint++ -- | Keeps the angular velocity ratio of a pair of bodies constant.+ , gearJointNew++ -- **** Properties+ , gearJointPhase+ , gearJointRatio++ -- *** Simple Motor++ -- | Keeps the relative angular velocity of a pair of bodies constant.+ -- You will usually want to set an force (torque) maximum for motors as otherwise+ -- they will be able to apply a nearly infinite torque to keep the bodies moving.+ , simpleMotorNew++ -- **** Properties+ , simpleMotorRate++ -- ** Notes++ -- | * You can add multiple joints between two bodies, but make sure that they don’t fight.+ -- Doing so can cause the bodies jitter or spin violently.++ -- * Overview of Collision Detection in Chipmunk++ -- | In order to make collision detection in Chipmunk as fast as possible, the process is broken down+ -- into several stages. While I’ve tried to keep it conceptually simple, the implementation can be a bit daunting.+ -- Fortunately as a user of the library, you don’t need to understand everything about how it works.+ -- Though if you are trying to squeeze every bit of performance out of Chipmunk, understanding this section+ -- can be helpful.++ -- ** Spatial Indexing++ -- | A for loop that checks every object against every other object in the scene would be very slow.+ -- So the first stage of the collision detection, commonly called the broadphase, uses a high level+ -- spatial algorithm to decide which pairs of objects to check for collisions.+ -- Currently Chipmunk supports two spatial indexes, an axis-aligned bounding box tree and a spatial hash.+ -- These spatial indexes are able to quickly identify which pairs of shapes are near each other+ -- and should be checked for a collision.++ -- ** Fast Collision Filtering++ -- | After the spatial index figures out which pairs of shapes are likely to be near each other,+ -- it passes each pair back to the space using a callback to perform some additional filtering on the pairs.+ -- Before doing anything else, Chipmunk performs a few quick tests to check if shapes should collide.+ --+ -- * __Bounding Box Test__: The shapes are not colliding if their bounding boxes are not overlapping.+ -- Objects like diagonal line segments can trigger a lot of false positives here,+ -- but it’s unlikely to be something you should worry about.+ --+ -- * __Category Mask Test__: The categories of each shape are bitwise ANDed against the category mask+ -- of the other shape. If either result is 0, the shapes do not collide.+ --+ -- * __Group Test__: Shapes shouldn’t collide with other shapes in the same non-zero group.++ -- ** Constraint Based Filtering++ -- | After fast collision filtering, Chipmunk checks the list of joints on one of the bodies+ -- to see if it has a constraint that attaches it to the other body. If that constraint’s collideBodies+ -- property is false, the collision will be ignored. This check is often very fast+ -- since most scenes don’t contain a lot of constraints.++ -- ** Primitive Shape to Shape Collision Detection++ -- | The most expensive test is to actually check for overlap based on their geometry.+ -- Circle to circle and circle to line collisions are very fast. Segment to segment and poly to poly collisions+ -- are handled using the GJK/EPA algorithms, and get more expensive as the number of vertexes increases.+ -- Simpler shapes make for faster collisions, and often more important, fewer collision points+ -- for the solver to run. Chipmunk uses a small dispatch table to figure out which function to use to check+ -- if the shapes overlap.+ --+ -- Without going into too much detail, the GJK algorithm checks the distance between two objects,+ -- and the EPA algorithm checks how much they are overlapping. If you give you segment and poly shapes+ -- a small radius when creating them, the EPA algorithm can usually be skipped, speeding up the collision detection+ -- considerably. The radius should be at least as big as the amount of allowed collision slop.++ -- ** Collision Handler Filtering++ -- | After checking if two shapes overlap Chipmunk will look to see if you have defined a collision handler+ -- for the collision types of the shapes. This is vital to process collisions events for the gameplay,+ -- but also gives you a very flexible way to filter out collisions. The return value of the begin and preSolve+ -- callbacks determines whether or not the colliding pair of shapes is discarded or not.+ -- Returning true will keep the pair, false will discard it. Rejecting a collision from a begin callback+ -- is permanent, rejecting it from the preSolve only applies to the step it occured in. If you don’t define+ -- a handler for the given collision types, Chipmunk will call the space’s default handler, which by default+ -- is defined to simply accept all collisions.+ --+ -- Wildcard collisions can also return a value, but they are handled in a more complicated way.+ -- When you create a collision handler between two specific collision types, it’s your responsibility+ -- to decide when to call the wildcard handlers and what to do with their return values.+ -- Otherwise, the default is to call the wildcard handler for the first type, then the second type,+ -- and use a logical AND of their return values as filtering value. See DefaultBegin() in cpSpace.c+ -- for more information.+ --+ -- While using callbacks to filter collisions is the most flexible way, keep in mind that by the time your callback+ -- is called all of the most expensive collision detection has already been done. For simulations+ -- with a lot of colliding objects each frame, the time spent finding collisions is small compared to the time+ -- spent solving the physics for them so it may not be a big deal. Fast collision filtering should be preferred+ -- if possible.++ -- * Collision Callbacks++ -- | A physics library without any events or feedback would not be very useful for games.+ -- How would you know when the player bumped into an enemy so that you could take some health points away?+ -- How would you know how hard the car hit something so you don’t play a loud crash noise when a pebble hits it?+ -- What if you need to decide if a collision should be ignored based on specific conditions,+ -- like implementing one way platforms? Chipmunk has a number of powerful callback systems+ -- that you can use to solve these problems.++ -- ** Collision Handlers++ -- | Collision handler function types. While all of them take an arbiter, space, and a user data pointer,+ -- only the begin and preSolve callbacks return a value. See above for more information.+ , CollisionCallback+ -- Collision callbacks are closely associated with 'Arbiter' structs.+ -- You should familiarize yourself with those as well.+ --+ -- __Note__: Shapes tagged as sensors (cpShape.sensor == true) never generate collisions that get processed,+ -- so collisions between sensors shapes and other shapes will never call the postSolve callback.+ -- They still generate begin, and separate callbacks, and the preSolve callback is also called+ -- every frame even though there is no collision response.+ --+ -- __Note #2__: preSolve callbacks are called before the sleeping algorithm runs.+ -- If an object falls asleep, its postSolve callback won’t be called until it’s reawoken.++ , CollisionType+ , CollisionHandler (..)+ , CollisionHandlerPtr+ , spaceAddCollisionHandler+ , spaceAddWildcardHandler+ , spaceAddDefaultCollisionHandler+ , modifyCollisionHandler+ , mkCallback+ , mkCallbackB++ -- ** Post-Step Callbacks++ -- | Post-step callbacks are the one place where you can break the rules about adding or removing objects+ -- from within a callback. In fact, their primary function is to help you safely remove objects from the space+ -- that you wanted to disable or destroy in a collision or query callback.+ --+ -- Post step callbacks are registered as a function and a pointer that is used as a key.+ -- You can only register one postStep callback per key. This prevents you from accidentally removing+ -- an object more than once. For instance, say that you get a collision callback between a bullet and object A.+ -- You want to destroy both the bullet and object A, so you register a postStep callback+ -- to safely remove them from your game. Then you get a second collision callback between the bullet and object B.+ -- You register a postStep callback to remove object B, and a second postStep callback to remove the bullet.+ -- Because you can only register one callback per key, the postStep callback for the bullet+ -- will only be called once and you can’t accidentally try to remove it twice.+ , PostStepFunc+ , spaceAddPostStepCallback++ -- ** Examples++ -- | See+ -- <https://chipmunk-physics.net/release/ChipmunkLatest-Docs/examples.html#CollisionCallbacks the callback examples>+ -- for more information.++ -- * Chipmunk Collision Pairs+ , Arbiter++ -- ** Memory Management++ -- | You will never need to create or free an arbiter. More importantly,+ -- because they are entirely managed by the space you should never store a reference to an arbiter+ -- as you don’t know when they will be freed or reused. Use them within the callback where they are given to you+ -- and then forget about them or copy out the information you need.++ -- ** Properties++ , arbiterRestitution+ , arbiterFriction+ , arbiterSurfaceVelocity+ , arbiterUserData++ -- *** Collision Point(s)+ , arbiterCount+ , arbiterNormal+ , arbiterPointA+ , arbiterPointB+ , arbiterDepth++ -- *** Other+ , arbiterIsFirstContact+ , arbiterIsRemoval++ -- *** Bodies and shapes+ , arbiterShapes+ , arbiterBodies++ -- *** Running wildcard handlers++ -- | These functions invoke the wildcard handlers for a given collision. For custom collision handlers+ -- between specific types or overriding the default handler, you must decide how to invoke the wildcard handlers+ -- since it may be important to call the wildcards first, last, or possibly skip them entirely.+ -- For the begin and preSolve callbacks, you also need to decide what to do with their return values+ -- since they may not agree with each other or the specific handler they were called from.+ -- Every collision handler is defined for two types, the “A” variants of these functions call the wildcard handler+ -- for the first type, and the “B” variants call the handler for the second type.++ , arbiterCallWildcardBeginA+ , arbiterCallWildcardBeginB+ , arbiterCallWildcardPreSolveA+ , arbiterCallWildcardPreSolveB+ , arbiterCallWildcardPostSolveA+ , arbiterCallWildcardPostSolveB+ , arbiterCallWildcardSeparateA+ , arbiterCallWildcardSeparateB++ -- * Misc+ , DataPtr+ , Transform (..)++ -- * Re-exports+ , nullPtr+ , HasGetter (..)+ , HasSetter (..)+ ) where++import Foreign+import Data.StateVar++import Chiphunk.Low.Types+import Chiphunk.Low.Math+import Chiphunk.Low.Helper+import Chiphunk.Low.Vect+import Chiphunk.Low.BB+import Chiphunk.Low.Body+import Chiphunk.Low.Shape+import Chiphunk.Low.Space+import Chiphunk.Low.Constraint+import Chiphunk.Low.Callback+import Chiphunk.Low.Arbiter
+ src/Chiphunk/Low/Arbiter.chs view
@@ -0,0 +1,172 @@+module Chiphunk.Low.Arbiter+ ( Arbiter+ , arbiterRestitution+ , arbiterFriction+ , arbiterSurfaceVelocity+ , arbiterUserData+ , arbiterCount+ , arbiterNormal+ , arbiterPointA+ , arbiterPointB+ , arbiterDepth+ , arbiterIsFirstContact+ , arbiterIsRemoval+ , arbiterShapes+ , arbiterBodies+ , arbiterCallWildcardBeginA+ , arbiterCallWildcardBeginB+ , arbiterCallWildcardPreSolveA+ , arbiterCallWildcardPreSolveB+ , arbiterCallWildcardPostSolveA+ , arbiterCallWildcardPostSolveB+ , arbiterCallWildcardSeparateA+ , arbiterCallWildcardSeparateB+ ) where++import Data.StateVar+import Foreign++{# import Chiphunk.Low.Types #}++#include <chipmunk/chipmunk.h>+#include <wrapper.h>++{# fun unsafe cpArbiterGetRestitution {`Arbiter'} -> `Double' #}++{# fun unsafe cpArbiterSetRestitution {`Arbiter', `Double'} -> `()' #}++-- | The calculated elasticity for this collision pair.+-- Setting the value in a preSolve() callback will override the value calculated by the space.+-- The default calculation multiplies the elasticity of the two shapes together.+arbiterRestitution :: Arbiter -> StateVar Double+arbiterRestitution = mkStateVar cpArbiterGetRestitution cpArbiterSetRestitution++{# fun unsafe cpArbiterGetFriction {`Arbiter'} -> `Double' #}++{# fun unsafe cpArbiterSetFriction {`Arbiter', `Double'} -> `()' #}++-- | The calculated friction for this collision pair.+-- Setting the value in a preSolve() callback will override the value calculated by the space.+-- The default calculation multiplies the friction of the two shapes together.+arbiterFriction :: Arbiter -> StateVar Double+arbiterFriction = mkStateVar cpArbiterGetFriction cpArbiterSetFriction++{# fun unsafe w_cpArbiterGetSurfaceVelocity {`Arbiter', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpArbiterSetSurfaceVelocity {`Arbiter', with* %`Vect'} -> `()' #}++-- | The calculated surface velocity for this collision pair.+-- Setting the value in a preSolve() callback will override the value calculated by the space.+-- The default calculation subtracts the surface velocity of the second shape+-- from the first and then projects that onto the tangent of the collision.+-- This is so that only friction is affected by default calculation.+--+-- Using a custom calculation, you can make something that responds like a pinball bumper,+-- or where the surface velocity is dependent on the location of the contact point.+arbiterSurfaceVelocity :: Arbiter -> StateVar Vect+arbiterSurfaceVelocity = mkStateVar w_cpArbiterGetSurfaceVelocity cpArbiterSetSurfaceVelocity++{# fun unsafe cpArbiterGetUserData {`Arbiter'} -> `DataPtr' #}++{# fun unsafe cpArbiterSetUserData {`Arbiter', `DataPtr'} -> `()' #}++-- | A user definable context pointer.+-- The value will persist until just after the separate callback is called for the pair.+--+-- __Note__: If you need to clean up this pointer, you should implement the separate callback to do it.+-- Also be careful when destroying the space as there may be active collisions still.+-- In order to trigger the separate callbacks and clean up your data,+-- you’ll need to remove all the shapes from the space before disposing of it.+-- This is something I’d suggest doing anyway.+-- See ChipmunkDemo.c:ChipmunkDemoFreeSpaceChildren() for an example of how to do it easily.+arbiterUserData :: Arbiter -> StateVar DataPtr+arbiterUserData = mkStateVar cpArbiterGetUserData cpArbiterSetUserData++{# fun unsafe cpArbiterGetCount {`Arbiter'} -> `Int' #}++-- | The number of contacts tracked by this arbiter.+-- For the forseeable future, the maximum number of contacts will be two.+arbiterCount :: Arbiter -> GettableStateVar Int+arbiterCount = makeGettableStateVar . cpArbiterGetCount++{# fun unsafe w_cpArbiterGetNormal {`Arbiter', alloca- `Vect' peek*} -> `()' #}++-- | Collision normal in a specific point tracked by this collision.+arbiterNormal :: Arbiter -> GettableStateVar Vect+arbiterNormal = makeGettableStateVar . w_cpArbiterGetNormal++{# fun unsafe w_cpArbiterGetPointA {`Arbiter', `Int', alloca- `Vect' peek*} -> `()' #}++-- | Collision point of a specific point on first body.+arbiterPointA :: Arbiter -> Int -> GettableStateVar Vect+arbiterPointA arbiter = makeGettableStateVar . w_cpArbiterGetPointA arbiter++{# fun unsafe w_cpArbiterGetPointB {`Arbiter', `Int', alloca- `Vect' peek*} -> `()' #}++-- | Collision point of a specific point on second body.+arbiterPointB :: Arbiter -> Int -> GettableStateVar Vect+arbiterPointB arbiter = makeGettableStateVar . w_cpArbiterGetPointB arbiter++{# fun unsafe cpArbiterGetDepth {`Arbiter', `Int'} -> `Double' #}++-- | Penetration depth of a collision point.+arbiterDepth :: Arbiter -> Int -> GettableStateVar Double+arbiterDepth arbiter = makeGettableStateVar . cpArbiterGetDepth arbiter++-- | Returns true if this is the first step the two shapes started touching. This can be useful for sound effects+-- for instance. If its the first frame for a certain collision, check the energy of the collision in a postStep+-- callbock and use that to determine the volume of a sound effect to play.+{# fun unsafe cpArbiterIsFirstContact as arbiterIsFirstContact {`Arbiter'} -> `Bool' #}++-- | Returns 'True' during a separate callback if the callback was invoked due to an object removal.+{# fun unsafe cpArbiterIsRemoval as arbiterIsRemoval {`Arbiter'} -> `Bool' #}++{# fun unsafe cpArbiterGetShapes+ { `Arbiter'+ , alloca- `Shape' peek*+ , alloca- `Shape' peek*+ } -> `()' #}++-- | The colliding shapes in the order that they were defined in the collision handler+-- associated with this arbiter.+-- If you defined the handler as cpSpaceAddCollisionHandler(space, 1, 2, ...),+-- you you will find that a->collision_type == 1 and b->collision_type == 2.+arbiterShapes :: Arbiter -> GettableStateVar (Shape, Shape)+arbiterShapes = makeGettableStateVar . cpArbiterGetShapes++{# fun unsafe cpArbiterGetBodies+ { `Arbiter'+ , alloca- `Body' peek*+ , alloca- `Body' peek*+ } -> `()' #}++-- | The colliding bodies in the order that they were defined in the collision handler+-- associated with this arbiter.+-- If you defined the handler as cpSpaceAddCollisionHandler(space, 1, 2, ...),+-- you you will find that a->collision_type == 1 and b->collision_type == 2.+arbiterBodies :: Arbiter -> GettableStateVar (Body, Body)+arbiterBodies = makeGettableStateVar . cpArbiterGetBodies++-- | Run begin wildcard callback for first body.+{# fun cpArbiterCallWildcardBeginA as arbiterCallWildcardBeginA {`Arbiter', `Space'} -> `Bool' #}++-- | Run begin wildcard callback for second body.+{# fun cpArbiterCallWildcardBeginB as arbiterCallWildcardBeginB {`Arbiter', `Space'} -> `Bool' #}++-- | Run preSolve wildcard callback for first body.+{# fun cpArbiterCallWildcardPreSolveA as arbiterCallWildcardPreSolveA {`Arbiter', `Space'} -> `Bool' #}++-- | Run preSolve wildcard callback for second body.+{# fun cpArbiterCallWildcardPreSolveB as arbiterCallWildcardPreSolveB {`Arbiter', `Space'} -> `Bool' #}++-- | Run postSolve wildcard callback for first body.+{# fun cpArbiterCallWildcardPostSolveA as arbiterCallWildcardPostSolveA {`Arbiter', `Space'} -> `()' #}++-- | Run postSolve wildcard callback for second body.+{# fun cpArbiterCallWildcardPostSolveB as arbiterCallWildcardPostSolveB {`Arbiter', `Space'} -> `()' #}++-- | Run separate wildcard callback for first body.+{# fun cpArbiterCallWildcardSeparateA as arbiterCallWildcardSeparateA {`Arbiter', `Space'} -> `()' #}++-- | Run separate wildcard callback for second body.+{# fun cpArbiterCallWildcardSeparateB as arbiterCallWildcardSeparateB {`Arbiter', `Space'} -> `()' #}
+ src/Chiphunk/Low/BB.chs view
@@ -0,0 +1,128 @@+-- | Description: Utilities for working with bounding box.+-- Module provides utilities for working with bounding box.+module Chiphunk.Low.BB+ ( BB (..)+ , bbNew+ , bbNewForExtents+ , bbNewForCircle+ , bbIntersects+ , bbContainsBB+ , bbContainsVect+ , bbMerge+ , bbExpand+ , bbCenter+ , bbArea+ , bbMergedArea+ , bbSegmentQuery+ , bbIntersectsSegment+ , bbClampVect+ , bbWrapVect+ ) where++import Data.Fixed+import Foreign++import Chiphunk.Low.Math++{# import Chiphunk.Low.Types #}++#include <chipmunk/chipmunk.h>+#include <wrapper.h>++-- | Convenience constructor for 'BB' structs.+bbNew :: Double -> Double -> Double -> Double -> BB+bbNew = BB++-- | Convenience constructor for making a 'BB' fitting with a center point and half width and height.+bbNewForExtents+ :: Vect -- ^ Center point+ -> Double -- ^ Half width+ -> Double -- ^ Half height+ -> BB+bbNewForExtents (Vect x y) hw hh = BB (x - hw) (y - hh) (x + hw) (y + hh)++-- | Convenience constructor for making a 'BB' fitting a circle at position @p@ with radius @r@.+bbNewForCircle+ :: Vect -- ^ p+ -> Double -- ^ r+ -> BB+bbNewForCircle v r = bbNewForExtents v r r++-- | Returns true if the bounding boxes intersect.+bbIntersects :: BB -> BB -> Bool+BB l1 b1 r1 t1 `bbIntersects` BB l2 b2 r2 t2 = r1 >= l1 && r2 >= l2 && t1 >= b1 && t2 >= b2++-- | Returns true if @bb@ completely contains @other@.+bbContainsBB+ :: BB -- ^ bb+ -> BB -- ^ other+ -> Bool+BB l1 b1 r1 t1 `bbContainsBB` BB l2 b2 r2 t2 = l1 <= l2 && r1 >= r2 && t1 >= t2 && b1 <= b2++-- | Returns true if @bb@ contains @v@.+bbContainsVect+ :: BB -- ^ bb+ -> Vect -- ^ v+ -> Bool+BB l b r t `bbContainsVect` Vect x y = l <= x && r >= x && b <= y && t >= y++-- | Return the minimal bounding box that contains both @a@ and @b@.+bbMerge+ :: BB -- ^ a+ -> BB -- ^ b+ -> BB+BB l1 b1 r1 t1 `bbMerge` BB l2 b2 r2 t2 = BB (min l1 l2) (min b1 b2) (max r1 r2) (max t1 t2)++-- | Return the minimal bounding box that contains both @bb@ and @v@.+bbExpand+ :: BB -- ^ bb+ -> Vect -- ^ v+ -> BB+BB l b r t `bbExpand` Vect x y = BB (min l x) (min b y) (max r x) (max t y)++-- | Return the center of @bb@.+bbCenter+ :: BB -- ^ bb+ -> Vect+bbCenter (BB l b r t) = Vect ((l + r) / 2) ((b + t) / 2)++-- | Return the area of @bb@.+bbArea+ :: BB -- ^ bb+ -> Double+bbArea (BB l b r t) = (r - l) * (t - b)++-- | Merges @a@ and @b@ then returns the area of the merged bounding box.+bbMergedArea+ :: BB -- ^ a+ -> BB -- ^ b+ -> Double+BB l1 b1 r1 t1 `bbMergedArea` BB l2 b2 r2 t2 = (max r1 r2 - min l1 l2) * (max t1 t2 - min b1 b2)++-- | Returns the fraction along the segment query the 'BB' is hit. Returns INFINITY if it doesn’t hit.+{# fun pure unsafe w_cpBBSegmentQuery as bbSegmentQuery+ { with* %`BB' -- ^ Box+ , with* %`Vect' -- ^ One segment end+ , with* %`Vect' -- ^ Other segment end+ } -> `Double' #}++-- | Returns true if the segment defined by endpoints @a@ and @b@ intersect @bb@.+{# fun pure unsafe w_cpBBIntersectsSegment as bbIntersectsSegment+ { with* %`BB' -- ^ bb+ , with* %`Vect' -- ^ a+ , with* %`Vect' -- ^ b+ } -> `Bool' #}++-- | Returns a copy of @v@ clamped to the bounding box @bb@.+bbClampVect+ :: BB -- ^ bb+ -> Vect -- ^ v+ -> Vect+BB l b r t `bbClampVect` Vect x y = Vect (fClamp x l r) (fClamp y b t)++-- | Returns a copy of @v@ wrapped to the bounding box @bb@.+bbWrapVect+ :: BB -- ^ bb+ -> Vect -- ^ v+ -> Vect+BB l b r t `bbWrapVect` Vect x y = Vect (l + ((x - l) `mod'` abs (r - l))) (b + ((y - b) `mod'` abs (t - b)))
+ src/Chiphunk/Low/Body.chs view
@@ -0,0 +1,319 @@+-- | Description: Rigid bodies manipulations+-- Module provides access to the rigid bodies which are at the core of the physics simulation.+module Chiphunk.Low.Body+ ( Body+ , BodyType (..)+ , bodyNew+ , bodyNewKinematic+ , bodyNewStatic+ , bodyFree+ , bodyType+ , bodyMass+ , bodyMoment+ , bodyPosition+ , bodyCenterOfGravity+ , bodyVelocity+ , bodyForce+ , bodyAngle+ , bodyAngularVelocity+ , bodyTorque+ , bodyRotation+ , bodySpace+ , bodyUserData+ , bodyLocalToWorld+ , bodyWorldToLocal+ , bodyVelocityAtWorldPoint+ , bodyVelocityAtLocalPoint+ , bodyApplyForceAtWorldPoint+ , bodyApplyForceAtLocalPoint+ , bodyApplyImpulseAtWorldPoint+ , bodyApplyImpulseAtLocalPoint+ , bodyIsSleeping+ , bodyActivate+ , bodySleep+ , bodyActivateStatic+ , bodySleepWithGroup+ , BodyShapeIteratorFunc+ , bodyEachShape+ , BodyConstraintIteratorFunc+ , bodyEachConstraint+ , BodyArbiterIteratorFunc+ , bodyEachArbiter+ ) where++import Chiphunk.Low.Vect+import Control.Exception.Safe+import Data.StateVar+import Foreign++{# import Chiphunk.Low.Types #}++#include <chipmunk/chipmunk.h>+#include <wrapper.h>++-- | Creates body of type 'BodyTypeDynamic'.+{# fun unsafe cpBodyNew as bodyNew+ { `Double' -- ^ Mass of the body. Guessing is usually fine.++ , `Double' -- ^ Moment of inertia of the body.+ -- Guessing a moment of inertia can lead to a very poor simulation+ -- so it’s recommended to use Chipmunk’s moment calculations+ -- to estimate the moment for you.+ } -> `Body' #}++-- | Create body of type 'BodyTypeKimenatic'.+{# fun unsafe cpBodyNewKinematic as bodyNewKinematic {} -> `Body' #}++-- | Create body of type 'BodyTypeStatic'.+{# fun unsafe cpBodyNewStatic as bodyNewStatic {} -> `Body' #}++-- | Be careful not to free a body before any shapes or constraints attached to it+-- have been removed from a space.+{# fun cpBodyFree as bodyFree {`Body'} -> `()' #}+-- no "unsafe" qualifier because I think it may trigger callbacks++-- | Get the type of a body (dynamic, kinematic, static).+{# fun unsafe cpBodyGetType {`Body'} -> `BodyType' #}++{# fun unsafe cpBodySetType {`Body', `BodyType'} -> `()' #}++-- | Type of a body (dynamic, kinematic, static).+-- When changing an body to a dynamic body, the mass and moment of inertia+-- are recalculated from the shapes added to the body.+-- Custom calculated moments of inertia are not preseved when changing types.+-- This function cannot be called directly in a collision callback.+bodyType :: Body -> StateVar BodyType+bodyType = mkStateVar cpBodyGetType cpBodySetType++{# fun unsafe cpBodyGetMass {`Body'} -> `Double' #}++{# fun unsafe cpBodySetMass {`Body', `Double'} -> `()' #}++-- | Mass of the body.+bodyMass :: Body -> StateVar Double+bodyMass = mkStateVar cpBodyGetMass cpBodySetMass++{# fun unsafe cpBodyGetMoment {`Body'} -> `Double' #}++{# fun unsafe cpBodySetMoment {`Body', `Double'} -> `()' #}++-- | Moment of inertia (MoI or sometimes just moment) of the body.+-- The moment is like the rotational mass of a body.+-- See below for function to help calculate the moment.+bodyMoment :: Body -> StateVar Double+bodyMoment = mkStateVar cpBodyGetMoment cpBodySetMoment++{# fun unsafe w_cpBodyGetPosition {`Body', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpBodySetPosition {`Body', with* %`Vect'} -> `()' #}++-- | Position of the body. When changing the position you may also want to call+-- 'spaceReindexShapesForBody' to update the collision detection information+-- for the attached shapes if plan to make any queries against the space.+bodyPosition :: Body -> StateVar Vect+bodyPosition = mkStateVar w_cpBodyGetPosition cpBodySetPosition++{# fun unsafe w_cpBodyGetCenterOfGravity {`Body', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpBodySetCenterOfGravity {`Body', with* %`Vect'} -> `()' #}++-- | Location of the center of gravity in body local coordinates.+-- The default value is (0, 0), meaning the center of gravity+-- is the same as the position of the body.+bodyCenterOfGravity :: Body -> StateVar Vect+bodyCenterOfGravity = mkStateVar w_cpBodyGetCenterOfGravity cpBodySetCenterOfGravity++{# fun unsafe w_cpBodyGetVelocity {`Body', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpBodySetVelocity {`Body', with* %`Vect'} -> `()' #}++-- | Linear velocity of the center of gravity of the body.+bodyVelocity :: Body -> StateVar Vect+bodyVelocity = mkStateVar w_cpBodyGetVelocity cpBodySetVelocity++{# fun unsafe w_cpBodyGetForce {`Body', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpBodySetForce {`Body', with* %`Vect'} -> `()' #}++-- | Force applied to the center of gravity of the body.+-- This value is reset for every time step.+bodyForce :: Body -> StateVar Vect+bodyForce = mkStateVar w_cpBodyGetForce cpBodySetForce++{# fun unsafe cpBodyGetAngle {`Body'} -> `Double' #}++{# fun unsafe cpBodySetAngle {`Body', `Double'} -> `()' #}++-- | Set rotation of the body in radians.+-- When changing the rotation you may also want to call 'spaceReindexShapesForBody'+-- to update the collision detection information for the attached shapes+-- if you plan to make any queries against the space.+-- A body rotates around its center of gravity, not its position.+bodyAngle :: Body -> StateVar Double+bodyAngle = mkStateVar cpBodyGetAngle cpBodySetAngle++{# fun unsafe cpBodyGetAngularVelocity {`Body'} -> `Double' #}++{# fun unsafe cpBodySetAngularVelocity {`Body', `Double'} -> `()' #}++-- | Angular velocity of the body in radians per second.+bodyAngularVelocity :: Body -> StateVar Double+bodyAngularVelocity = mkStateVar cpBodyGetAngularVelocity cpBodySetAngularVelocity++{# fun unsafe cpBodyGetTorque {`Body'} -> `Double' #}++{# fun unsafe cpBodySetTorque {`Body', `Double'} -> `()' #}++-- | Torque applied to the body. This value is reset for every time step.+bodyTorque :: Body -> StateVar Double+bodyTorque = mkStateVar cpBodyGetTorque cpBodySetTorque++{# fun unsafe w_cpBodyGetRotation {`Body', alloca- `Vect' peek*} -> `()' #}++-- | The rotation vector for the body.+-- Can be used with 'vRotate' or 'vUnRotate' to perform fast rotations.+bodyRotation :: Body -> GettableStateVar Vect+bodyRotation = makeGettableStateVar . w_cpBodyGetRotation++{# fun unsafe cpBodyGetSpace {`Body'} -> `Space' #}++-- | The 'Space' that body has been added to.+bodySpace :: Body -> GettableStateVar Space+bodySpace = makeGettableStateVar . cpBodyGetSpace++{# fun unsafe cpBodyGetUserData {`Body'} -> `DataPtr' #}++{# fun unsafe cpBodySetUserData {`Body', `DataPtr'} -> `()' #}++-- | User data pointer. Use this pointer to get a reference to the game object+-- that owns this body from callbacks.+bodyUserData :: Body -> StateVar DataPtr+bodyUserData = mkStateVar cpBodyGetUserData cpBodySetUserData++-- | Convert from body local coordinates to world space coordinates.+{# fun unsafe w_cpBodyLocalToWorld as bodyLocalToWorld {`Body', with* %`Vect', alloca- `Vect' peek*} -> `()' #}++-- | Convert from world space coordinates to body local coordinates.+{# fun unsafe w_cpBodyWorldToLocal as bodyWorldToLocal {`Body', with* %`Vect', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe w_cpBodyGetVelocityAtWorldPoint+ {`Body', with* %`Vect', alloca- `Vect' peek*} -> `()' #}++-- | Absolute velocity of the rigid body at the given world point.+bodyVelocityAtWorldPoint :: Body -> Vect -> GettableStateVar Vect+bodyVelocityAtWorldPoint body = makeGettableStateVar . w_cpBodyGetVelocityAtWorldPoint body++{# fun unsafe w_cpBodyGetVelocityAtLocalPoint+ {`Body', with* %`Vect', alloca- `Vect' peek*} -> `()' #}++-- | Absolute velocity of the rigid body at the given body local point.+bodyVelocityAtLocalPoint :: Body -> Vect -> GettableStateVar Vect+bodyVelocityAtLocalPoint body = makeGettableStateVar . w_cpBodyGetVelocityAtLocalPoint body++-- | Add the @force@ to @body@ as if applied from the world @point@.+{# fun unsafe cpBodyApplyForceAtWorldPoint as bodyApplyForceAtWorldPoint+ { `Body' -- ^ body+ , with* %`Vect' -- ^ force+ , with* %`Vect' -- ^ point+ } -> `()' #}++-- | Add the local @force@ to @body@ as if applied from the body local @point@.+{# fun unsafe cpBodyApplyForceAtLocalPoint as bodyApplyForceAtLocalPoint+ { `Body' -- ^ body+ , with* %`Vect' -- ^ force+ , with* %`Vect' -- ^ point+ } -> `()' #}++-- | Add the @impulse@ to @body@ as if applied from the world @ point@.+{# fun unsafe cpBodyApplyImpulseAtWorldPoint as bodyApplyImpulseAtWorldPoint+ { `Body' -- ^ body+ , with* %`Vect' -- ^ impulse+ , with* %`Vect' -- ^ point+ } -> `()' #}++-- | Add the local @impulse@ to @body@ as if applied from the body local @point@.+{# fun unsafe cpBodyApplyImpulseAtLocalPoint as bodyApplyImpulseAtLocalPoint+ { `Body' -- ^ body+ , with* %`Vect' -- ^ impulse+ , with* %`Vect' -- ^ point+ } -> `()' #}++-- | Returns true if body is sleeping.+{# fun unsafe cpBodyIsSleeping as bodyIsSleeping {`Body'} -> `Bool' #}++-- | Reset the idle timer on a body. If it was sleeping, wake it and any other bodies it was touching.+{# fun unsafe cpBodyActivate as bodyActivate {`Body'} -> `()' #}++-- | Forces a body to fall asleep immediately even if it’s in midair. Cannot be called from a callback.+{# fun unsafe cpBodySleep as bodySleep {`Body'} -> `()' #}++-- | Activates all bodies touching @body@.+-- If @filter@ is not 'nullPtr', then only bodies touching through @filter@ will be awoken.+{# fun unsafe cpBodyActivateStatic as bodyActivateStatic+ { `Body' -- ^ body+ , `Shape' -- ^ filter+ } -> `()' #}++-- | When objects in Chipmunk sleep, they sleep as a group of all objects that are touching or jointed together.+-- When an object is woken up, all of the objects in its group are woken up.+-- 'bodySleepWithGroup' allows you group sleeping objects together.+-- It acts identically to 'bodySleep' if you pass 'nullPtr' as @group@ by starting a new group.+-- If you pass a sleeping body for @group@, @body@ will be awoken when group is awoken.+-- You can use this to initialize levels and start stacks of objects in a pre-sleeping state.+{# fun unsafe cpBodySleepWithGroup as bodySleepWithGroup+ { `Body' -- ^ body+ , `Body' -- ^ group+ } -> `()' #}++-- | Type of callback which can be used to iterate all 'Shape's in a 'Body'.+type BodyShapeIteratorFunc = Body -> Shape -> Ptr () -> IO ()++foreign import ccall unsafe "wrapper"+ mkBodyShapeIteratorFunc :: BodyShapeIteratorFunc -> IO (FunPtr BodyShapeIteratorFunc)++-- | Call @func@ once for each shape that is attached to @body@ and added to a space.+-- @data@ is passed along as a context value. It is safe to remove shapes using these callbacks.+{# fun cpBodyEachShape as bodyEachShape+ { `Body' -- ^ body+ , withIterator* `BodyShapeIteratorFunc' -- ^ func+ , `Ptr ()' -- ^ data+ } -> `()' #}+ where+ withIterator i = mkBodyShapeIteratorFunc i `bracket` freeHaskellFunPtr++-- | Type of callback which can be used to iterate all 'Constraint's in a 'Body'.+type BodyConstraintIteratorFunc = Body -> Constraint -> Ptr () -> IO ()++foreign import ccall unsafe "wrapper"+ mkBodyConstraintIteratorFunc :: BodyConstraintIteratorFunc -> IO (FunPtr BodyConstraintIteratorFunc)++-- | Call @func@ once for each constraint that is attached to @body@ and added to a space.+-- @data@ is passed along as a context value. It is safe to remove constraints using thes callbacks.+{# fun cpBodyEachConstraint as bodyEachConstraint+ { `Body' -- ^ body+ , withIterator* `BodyConstraintIteratorFunc' -- ^ func+ , `Ptr ()' -- ^ data+ } -> `()' #}+ where+ withIterator i = mkBodyConstraintIteratorFunc i `bracket` freeHaskellFunPtr++-- | Type of callback which can be used to iterate all 'Arbiter's in a 'Body'.+type BodyArbiterIteratorFunc = Body -> Arbiter -> Ptr () -> IO ()++foreign import ccall unsafe "wrapper"+ mkBodyArbiterIteratorFunc :: BodyArbiterIteratorFunc -> IO (FunPtr BodyArbiterIteratorFunc)++-- | This one is more interesting. Calls @func@ once for each collision pair that @body@ is involved in.+-- Calling 'arbiterGetBodies'/'arbiterGetShapes' will return the body or shape for body as the first argument.+-- You can use this to check all sorts of collision information for a body like if it’s touching the ground,+-- another particular object, how much collision force is being applied to an object, etc.+--+-- Sensor shapes and arbiters that have been rejected by a collision handler callback or 'arbiterIgnore'+-- are not tracked by the contact graph.+{# fun cpBodyEachArbiter as bodyEachArbiter+ { `Body' -- ^ body+ , withIterator* `BodyArbiterIteratorFunc' -- ^ func+ , `Ptr ()' -- ^ data+ } -> `()' #}+ where+ withIterator i = mkBodyArbiterIteratorFunc i `bracket` freeHaskellFunPtr
+ src/Chiphunk/Low/Callback.chs view
@@ -0,0 +1,159 @@+-- | Description: Collision handler definition+-- Module provides definitions for collision handlers.+module Chiphunk.Low.Callback+ ( CollisionCallback+ , CollisionHandler (..)+ , CollisionHandlerPtr+ , spaceAddCollisionHandler+ , spaceAddWildcardHandler+ , spaceAddDefaultCollisionHandler+ , modifyCollisionHandler+ , mkCallback+ , mkCallbackB+ , PostStepFunc+ , spaceAddPostStepCallback+ ) where++import Control.Applicative (liftA)+import Control.Exception.Safe+import Data.Bool (bool)+import Foreign++{# import Chiphunk.Low.Types #}++#include <chipmunk/chipmunk.h>++-- | Collision callback+type CollisionCallback ret = Arbiter -> Space -> DataPtr -> IO ret++-- | This collision handler processes collisions between objects of type @typeA@ and @typeB@.+-- Fill the desired collision callback functions- they are documented above. A user definable context pointer+-- @userData@ is included for your convenience. This pointer is provided as an argument in each callback function.+--+-- A collision handler is a set of 4 function callbacks for the different collision events that Chipmunk recognizes.+data CollisionHandler = CollisionHandler+ { chTypeA :: !CollisionType -- ^ typeA+ , chTypeB :: !CollisionType -- ^ typeB+ , chBeginFunc :: !(FunPtr (CollisionCallback CPBool))+ -- ^ Two shapes just started touching for the first time this step. Return true from the callback+ -- to process the collision normally or false to cause Chipmunk to ignore the collision entirely.+ -- If you return false, the preSolve and postSolve callbacks will never be run, but you will still recieve+ -- a separate event when the shapes stop overlapping.+ , chPreSolveFunc :: !(FunPtr (CollisionCallback CPBool))+ -- ^ Two shapes are touching during this step. Return false from the callback to make Chipmunk ignore the collision+ -- this step or true to process it normally. Additionally, you may override collision values using+ -- 'Chiphunk.Low.Arbiter.arbiterFriction', 'Chiphunk.Low.Arbiter.arbiterRestitution' or+ -- 'Chiphunk.Low.arbiterSurfaceVelocity' to provide custom friction, elasticity, or surface velocity values.+ -- See 'Arbiter' for more info.+ , chPostSolveFunc :: !(FunPtr (CollisionCallback ()))+ -- ^ Two shapes are touching and their collision response has been processed. You can retrieve the collision+ -- impulse or kinetic energy at this time if you want to use it to calculate sound volumes or damage amounts.+ -- See 'Arbiter' for more info.+ , chSeparateFunc :: !(FunPtr (CollisionCallback ()))+ -- ^ Two shapes have just stopped touching for the first time this step. To ensure that begin/separate+ -- are always called in balanced pairs, it will also be called when removing a shape while its in contact+ -- with something or when deallocating the space.+ , cpUserData :: !DataPtr -- ^ userData+ } deriving Show++instance Storable CollisionHandler where+ sizeOf _ = {# sizeof cpCollisionHandler #}+ alignment _ = {# alignof cpCollisionHandler #}+ poke p (CollisionHandler typA typB beginFunc preSolveFunc postSolveFunc separateFunc userData) = do+ {# set cpCollisionHandler->typeA #} p $ fromIntegral typA+ {# set cpCollisionHandler->typeB #} p $ fromIntegral typB+ {# set cpCollisionHandler->beginFunc #} p beginFunc+ {# set cpCollisionHandler->preSolveFunc #} p preSolveFunc+ {# set cpCollisionHandler->postSolveFunc #} p postSolveFunc+ {# set cpCollisionHandler->separateFunc #} p separateFunc+ {# set cpCollisionHandler->userData #} p userData+ peek p = CollisionHandler <$> (fromIntegral <$> {# get cpCollisionHandler->typeA #} p)+ <*> (fromIntegral <$> {# get cpCollisionHandler->typeB #} p)+ <*> {# get cpCollisionHandler->beginFunc #} p+ <*> {# get cpCollisionHandler->preSolveFunc #} p+ <*> {# get cpCollisionHandler->postSolveFunc #} p+ <*> {# get cpCollisionHandler->separateFunc #} p+ <*> {# get cpCollisionHandler->userData #} p++foreign import ccall unsafe "wrapper"+ mkCallback' :: CollisionCallback () -> IO (FunPtr (CollisionCallback ()))++-- | Make callback. Need to free afterwards.+mkCallback :: CollisionCallback () -> IO (FunPtr (CollisionCallback ()))+mkCallback = mkCallback'++foreign import ccall unsafe "wrapper"+ mkCallbackB' :: CollisionCallback CPBool -> IO (FunPtr (CollisionCallback CPBool))++-- | Make callback. Need to free afterwards.+mkCallbackB :: CollisionCallback Bool -> IO (FunPtr (CollisionCallback CPBool))+mkCallbackB = mkCallbackB' . liftA (liftA $ liftA $ liftA $ bool 0 1)++-- | Pointer to collision handler+{# pointer *cpCollisionHandler as CollisionHandlerPtr -> CollisionHandler #}++-- | Add a 'CollisionHandler' for specific collision type pair or return the existing handler for the type pair.+-- Whenever shapes with collision types (cpShape.collision_type) a and b collide,+-- this handler will be used to process the collision events. When a new collision handler is created,+-- the callbacks will all be set to builtin callbacks that perform the default behavior+-- (call the wildcard handlers, and accept all collisions).+{# fun unsafe cpSpaceAddCollisionHandler as spaceAddCollisionHandler+ { `Space'+ , fromIntegral `CollisionType' -- ^ a+ , fromIntegral `CollisionType' -- ^ b+ } -> `CollisionHandlerPtr' #}++-- | Add a wildcard collision handler for given collision type. This handler will be used any time an object+-- with this type collides with another object, regardless of its type. A good example is a projectile+-- that should be destroyed the first time it hits anything. There may be a specific collision handler+-- and two wildcard handlers. It’s up to the specific handler to decide if and when to call the wildcard handlers+-- and what to do with their return values. (See arbiterCallWildcard* below)+-- When a new wildcard handler is created, the callbacks will all be set to builtin callbacks+-- that perform the default behavior. (accept all collisions in begin and preSolve, or do nothing for postSolve+-- and separate.+{# fun unsafe cpSpaceAddWildcardHandler as spaceAddWildcardHandler+ { `Space'+ , fromIntegral `CollisionType' -- ^ type+ } -> `CollisionHandlerPtr' #}++-- | Return a reference to the default collision handler or that is used to process all collisions+-- that don’t have a more specific handler. The default behavior for each of the callbacks+-- is to call the wildcard handlers, ANDing their return values together if applicable.+{# fun unsafe cpSpaceAddDefaultCollisionHandler as spaceAddDefaultCollisionHandler+ { `Space'+ } -> `CollisionHandlerPtr' #}++-- | Use this helper function to modify collision handler.+--+-- @+-- spaceAddCollisionHandler s t1 t2 >>= flip modifyColliionHandler (\ch -> pure ch {chSeparateFunc = separateCollback})+-- @+modifyCollisionHandler :: CollisionHandlerPtr -> (CollisionHandler -> IO CollisionHandler) -> IO ()+modifyCollisionHandler chPtr inner = peek chPtr >>= inner >>= poke chPtr++-- | Function type used for postStep callbacks. @space@ is the space the callback was registered on,+-- @obj@ is the pointer value you supplied as the key, and @data@ is a user definable pointer you can use+-- to pass in as a context value.+type PostStepFunc+ = Space -- ^ space+ -> Ptr () -- ^ obj+ -> Ptr () -- ^ data+ -> IO ()++foreign import ccall "wrapper"+ mkPostStep :: PostStepFunc -> IO (FunPtr PostStepFunc)++-- | Add @func@ to be called before 'spaceStep' returns. @key@ and @data@ will be passed to your function.+-- Only the first callback registered for any unique value of @key@ will be recorded.+--+-- It returns 'True' if the callback is scheduled and 'False' when the key has already been used.+--+-- __The behavior of adding a postStep callback from outside of a collision handler or query callback is undefined.__+{# fun unsafe cpSpaceAddPostStepCallback as spaceAddPostStepCallback+ { `Space' -- ^ space+ , mk* `PostStepFunc' -- ^ func+ , `Ptr ()' -- ^ key+ , `Ptr ()' -- ^ data+ } -> `Bool' #}+ where+ mk f = mkPostStep f `bracket` freeHaskellFunPtr
+ src/Chiphunk/Low/Constraint.chs view
@@ -0,0 +1,467 @@+-- | Description: Dealing with joints/constraints+-- Module defines utilities for operations with constraints.+module Chiphunk.Low.Constraint+ ( Constraint+ , constraintBodyA+ , constraintBodyB+ , constraintMaxForce+ , constraintErrorBias+ , constraintMaxBias+ , constraintSpace+ , constraintCollideBodies+ , constraintUserData+ , constraintImpulse+ , constraintFree+ , pinJointNew+ , pinJointAnchorA+ , pinJointAnchorB+ , pinJointDist+ , slideJointNew+ , slideJointAnchorA+ , slideJointAnchorB+ , slideJointMin+ , slideJointMax+ , pivotJointNew+ , pivotJointNew2+ , pivotJointAnchorA+ , pivotJointAnchorB+ , grooveJointNew+ , grooveJointGrooveA+ , grooveJointGrooveB+ , grooveJointAnchorB+ , dampedSpringNew+ , dampedSpringAnchorA+ , dampedSpringAnchorB+ , dampedSpringRestLength+ , dampedSpringStiffness+ , dampedSpringDamping+ , dampedRotarySpringNew+ , dampedRotarySpringRestAngle+ , dampedRotarySpringStiffness+ , dampedRotarySpringDamping+ , rotaryLimitJointNew+ , rotaryLimitJointMin+ , rotaryLimitJointMax+ , ratchetJointNew+ , ratchetJointAngle+ , ratchetJointPhase+ , ratchetJointRatchet+ , gearJointNew+ , gearJointPhase+ , gearJointRatio++ , simpleMotorNew+ , simpleMotorRate+ ) where++import Data.StateVar+import Foreign++{# import Chiphunk.Low.Types #}++#include <chipmunk/chipmunk.h>+#include <wrapper.h>++{# fun unsafe cpConstraintGetBodyA {`Constraint'} -> `Body' #}++-- | The first body constraint is attached to+constraintBodyA :: Constraint -> GettableStateVar Body+constraintBodyA = makeGettableStateVar . cpConstraintGetBodyA++{# fun unsafe cpConstraintGetBodyB {`Constraint'} -> `Body' #}++-- | The second body constraint is attached to+constraintBodyB :: Constraint -> GettableStateVar Body+constraintBodyB = makeGettableStateVar . cpConstraintGetBodyB++{# fun unsafe cpConstraintGetMaxForce {`Constraint'} -> `Double' #}++{# fun unsafe cpConstraintSetMaxForce {`Constraint', `Double'} -> `()' #}++-- | The maximum force that the constraint can use to act on the two bodies.+-- Defaults to INFINITY.+constraintMaxForce :: Constraint -> StateVar Double+constraintMaxForce = mkStateVar cpConstraintGetMaxForce cpConstraintSetMaxForce++{# fun unsafe cpConstraintGetErrorBias {`Constraint'} -> `Double' #}++{# fun unsafe cpConstraintSetErrorBias {`Constraint', `Double'} -> `()' #}++-- | The percentage of joint error that remains unfixed after a second.+-- This works exactly the same as the collision bias property of a space,+-- but applies to fixing error (stretching) of joints instead of overlapping collisions.+constraintErrorBias :: Constraint -> StateVar Double+constraintErrorBias = mkStateVar cpConstraintGetErrorBias cpConstraintSetErrorBias++{# fun unsafe cpConstraintGetMaxBias {`Constraint'} -> `Double' #}++{# fun unsafe cpConstraintSetMaxBias {`Constraint', `Double'} -> `()' #}++-- | Get the maximum speed at which the constraint can apply error correction.+-- Defaults to INFINITY.+constraintMaxBias :: Constraint -> StateVar Double+constraintMaxBias = mkStateVar cpConstraintGetMaxBias cpConstraintSetMaxBias++{# fun unsafe cpConstraintGetSpace {`Constraint'} -> `Space' #}++-- | The 'Space' that constraint has been added to.+constraintSpace :: Constraint -> GettableStateVar Space+constraintSpace = makeGettableStateVar . cpConstraintGetSpace++{# fun unsafe cpConstraintGetCollideBodies {`Constraint'} -> `Bool' #}++{# fun unsafe cpConstraintSetCollideBodies {`Constraint', `Bool'} -> `()' #}++-- | Constraints can be used for filtering collisions too.+-- When two bodies collide, Chipmunk ignores the collisions+-- if this property is set to @False@ on any constraint that connects the two bodies.+-- Defaults to @True@.+--+-- This can be used to create a chain that self collides,+-- but adjacent links in the chain do not collide.+constraintCollideBodies :: Constraint -> StateVar Bool+constraintCollideBodies = mkStateVar cpConstraintGetCollideBodies cpConstraintSetCollideBodies++{# fun unsafe cpConstraintGetUserData {`Constraint'} -> `DataPtr' #}++{# fun unsafe cpConstraintSetUserData {`Constraint', `DataPtr'} -> `()' #}++-- | A user definable data pointer.+-- Use this pointer to get a reference to the game object that owns this constraint+-- from callbacks.+constraintUserData :: Constraint -> StateVar DataPtr+constraintUserData = mkStateVar cpConstraintGetUserData cpConstraintSetUserData++{# fun unsafe cpConstraintGetImpulse {`Constraint'} -> `Double' #}++-- | The most recent impulse that constraint applied.+-- To convert this to a force, divide by the timestep passed to 'spaceStep'.+-- You can use this to implement breakable joints to check+-- if the force they attempted to apply exceeded a certain threshold.+constraintImpulse :: Constraint -> GettableStateVar Double+constraintImpulse = makeGettableStateVar . cpConstraintGetImpulse++-- | Free function is shared by all joint types. Allocation functions are specific to each joint type.+{# fun cpConstraintFree as constraintFree {`Constraint'} -> `()' #}+-- no "unsafe" qualifier because I think it may trigger separte callbacks++-- | Connect two bodies via anchor points on those bodies. The distance between the two anchor points is measured+-- when the joint is created. If you want to set a specific distance, use the setter function to override it.+{# fun unsafe cpPinJointNew as pinJointNew+ { `Body' -- ^ First body to connect+ , `Body' -- ^ Second body to connect+ , with* %`Vect' -- ^ First anchor+ , with* %`Vect' -- ^ Second anchor+ } -> `Constraint' #}++{# fun unsafe w_cpPinJointGetAnchorA {`Constraint', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpPinJointSetAnchorA {`Constraint', with* %`Vect'} -> `()' #}++-- | Anchor on first body.+pinJointAnchorA :: Constraint -> StateVar Vect+pinJointAnchorA = mkStateVar w_cpPinJointGetAnchorA cpPinJointSetAnchorA++{# fun unsafe w_cpPinJointGetAnchorB {`Constraint', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpPinJointSetAnchorB {`Constraint', with* %`Vect'} -> `()' #}++-- | Anchor on second body.+pinJointAnchorB :: Constraint -> StateVar Vect+pinJointAnchorB = mkStateVar w_cpPinJointGetAnchorB cpPinJointSetAnchorB++{# fun unsafe cpPinJointGetDist {`Constraint'} -> `Double' #}++{# fun unsafe cpPinJointSetDist {`Constraint', `Double'} -> `()' #}++-- | Desired distance the joint will try to enforce.+pinJointDist :: Constraint -> StateVar Double+pinJointDist = mkStateVar cpPinJointGetDist cpPinJointSetDist++-- | Connect two bodies via anchor points forcing distance to remain in range.+{# fun unsafe cpSlideJointNew as slideJointNew+ { `Body' -- ^ First body to connect+ , `Body' -- ^ Second body to connect+ , with* %`Vect' -- ^ First anchor+ , with* %`Vect' -- ^ Second anchor+ , `Double' -- ^ Minimum allowed distance+ , `Double' -- ^ Maximum allowed distance+ } -> `Constraint' #}++{# fun unsafe w_cpSlideJointGetAnchorA {`Constraint', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpSlideJointSetAnchorA {`Constraint', with* %`Vect'} -> `()' #}++-- | Anchor on first body.+slideJointAnchorA :: Constraint -> StateVar Vect+slideJointAnchorA = mkStateVar w_cpSlideJointGetAnchorA cpSlideJointSetAnchorA++{# fun unsafe w_cpSlideJointGetAnchorB {`Constraint', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpSlideJointSetAnchorB {`Constraint', with* %`Vect'} -> `()' #}++-- | Anchor on second body.+slideJointAnchorB :: Constraint -> StateVar Vect+slideJointAnchorB = mkStateVar w_cpSlideJointGetAnchorB cpSlideJointSetAnchorB++{# fun unsafe cpSlideJointGetMin {`Constraint'} -> `Double' #}++{# fun unsafe cpSlideJointSetMin {`Constraint', `Double'} -> `()' #}++-- | The minimum distance the joint will try to enforce.+slideJointMin :: Constraint -> StateVar Double+slideJointMin = mkStateVar cpSlideJointGetMin cpSlideJointSetMin++{# fun unsafe cpSlideJointGetMax {`Constraint'} -> `Double' #}++{# fun unsafe cpSlideJointSetMax {`Constraint', `Double'} -> `()' #}++-- | The maximum distance the joint will try to enforce.+slideJointMax :: Constraint -> StateVar Double+slideJointMax = mkStateVar cpSlideJointGetMax cpSlideJointSetMax++-- | Because the pivot location is given in world coordinates,+-- you must have the bodies moved into the correct positions already.+{# fun unsafe cpPivotJointNew as pivotJointNew+ { `Body' -- ^ First body to connect+ , `Body' -- ^ Second body to connect+ , with* %`Vect' -- ^ Point in the world coordinates of the pivot+ } -> `Constraint' #}++-- | Alternatively you can specify the joint based on a pair of anchor points,+-- but make sure you have the bodies in the right place as the joint will fix itself+-- as soon as you start simulating the space.+{# fun unsafe cpPivotJointNew2 as pivotJointNew2+ { `Body' -- ^ First body to connect+ , `Body' -- ^ Second body to connect+ , with* %`Vect' -- ^ Anchor on first body+ , with* %`Vect' -- ^ Anchor on second body+ } -> `Constraint' #}++{# fun unsafe w_cpPivotJointGetAnchorA {`Constraint', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpPivotJointSetAnchorA {`Constraint', with* %`Vect'} -> `()' #}++-- | Anchor on first body.+pivotJointAnchorA :: Constraint -> StateVar Vect+pivotJointAnchorA = mkStateVar w_cpPivotJointGetAnchorA cpPivotJointSetAnchorA++{# fun unsafe w_cpPivotJointGetAnchorB {`Constraint', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpPivotJointSetAnchorB {`Constraint', with* %`Vect'} -> `()' #}++-- | Anchor on second body.+pivotJointAnchorB :: Constraint -> StateVar Vect+pivotJointAnchorB = mkStateVar w_cpPivotJointGetAnchorB cpPivotJointSetAnchorB++-- | Pivot is attached to groove on first body and to anchor on the second. All coordinates are body local.+{# fun unsafe cpGrooveJointNew as grooveJointNew+ { `Body' -- ^ First body to connect+ , `Body' -- ^ Second body to connect+ , with* %`Vect' -- ^ First endpoint of groove (on first body)+ , with* %`Vect' -- ^ Second endpoint of groove (on first body)+ , with* %`Vect' -- ^ Anchor (on second body)+ } -> `Constraint' #}++{# fun unsafe w_cpGrooveJointGetGrooveA {`Constraint', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpGrooveJointSetGrooveA {`Constraint', with* %`Vect'} -> `()' #}++-- | First endpoint of groove (on first body).+grooveJointGrooveA :: Constraint -> StateVar Vect+grooveJointGrooveA = mkStateVar w_cpGrooveJointGetGrooveA cpGrooveJointSetGrooveA++{# fun unsafe w_cpGrooveJointGetGrooveB {`Constraint', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpGrooveJointSetGrooveB {`Constraint', with* %`Vect'} -> `()' #}++-- | Second endpoint of groove (on first body).+grooveJointGrooveB :: Constraint -> StateVar Vect+grooveJointGrooveB = mkStateVar w_cpGrooveJointGetGrooveB cpGrooveJointSetGrooveB++{# fun unsafe w_cpGrooveJointGetAnchorB {`Constraint', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpGrooveJointSetAnchorB {`Constraint', with* %`Vect'} -> `()' #}++-- | Anchor on second body.+grooveJointAnchorB :: Constraint -> StateVar Vect+grooveJointAnchorB = mkStateVar w_cpGrooveJointGetAnchorB cpGrooveJointSetAnchorB++-- | Defined much like a slide joint.+{# fun unsafe cpDampedSpringNew as dampedSpringNew+ { `Body' -- ^ First body to connect+ , `Body' -- ^ Second body to connect+ , with* %`Vect' -- ^ First anchor+ , with* %`Vect' -- ^ Second anchor+ , `Double' -- ^ Distance the spring wants to be+ , `Double' -- ^ Spring constant (<http://en.wikipedia.org/wiki/Young%27s_modulus Young's modulus>)+ , `Double' -- ^ How soft to make damping of the spring+ } -> `Constraint' #}++{# fun unsafe w_cpDampedSpringGetAnchorA {`Constraint', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpDampedSpringSetAnchorA {`Constraint', with* %`Vect'} -> `()' #}++-- | Anchor on first body.+dampedSpringAnchorA :: Constraint -> StateVar Vect+dampedSpringAnchorA = mkStateVar w_cpDampedSpringGetAnchorA cpDampedSpringSetAnchorA++{# fun unsafe w_cpDampedSpringGetAnchorB {`Constraint', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpDampedSpringSetAnchorB {`Constraint', with* %`Vect'} -> `()' #}++-- | Anchor on second body.+dampedSpringAnchorB :: Constraint -> StateVar Vect+dampedSpringAnchorB = mkStateVar w_cpDampedSpringGetAnchorB cpDampedSpringSetAnchorB++{# fun unsafe cpDampedSpringGetRestLength {`Constraint'} -> `Double' #}++{# fun unsafe cpDampedSpringSetRestLength {`Constraint', `Double'} -> `()' #}++-- | Desired distance the spring will try to enforce.+dampedSpringRestLength :: Constraint -> StateVar Double+dampedSpringRestLength = mkStateVar cpDampedSpringGetRestLength cpDampedSpringSetRestLength++{# fun unsafe cpDampedSpringGetStiffness {`Constraint'} -> `Double' #}++{# fun unsafe cpDampedSpringSetStiffness {`Constraint', `Double'} -> `()' #}++-- | Spring stiffness+dampedSpringStiffness :: Constraint -> StateVar Double+dampedSpringStiffness = mkStateVar cpDampedSpringGetStiffness cpDampedSpringSetStiffness++{# fun unsafe cpDampedSpringGetDamping {`Constraint'} -> `Double' #}++{# fun unsafe cpDampedSpringSetDamping {`Constraint', `Double'} -> `()' #}++-- | Spring damping+dampedSpringDamping :: Constraint -> StateVar Double+dampedSpringDamping = mkStateVar cpDampedSpringGetDamping cpDampedSpringSetDamping++-- | Create new damped rotary spring constraint+{# fun unsafe cpDampedRotarySpringNew as dampedRotarySpringNew+ { `Body' -- ^ First body to connect+ , `Body' -- ^ Second body to connect+ , `Double' -- ^ Relative angle in radians that the bodies want to have+ , `Double' -- ^ Spring constant (stiffness)+ , `Double' -- ^ Spring damping+ } -> `Constraint' #}++{# fun unsafe cpDampedRotarySpringGetRestAngle {`Constraint'} -> `Double' #}++{# fun unsafe cpDampedRotarySpringSetRestAngle {`Constraint', `Double'} -> `()' #}++-- | Set desired angle in radians the spring will try to enforce.+dampedRotarySpringRestAngle :: Constraint -> StateVar Double+dampedRotarySpringRestAngle = mkStateVar cpDampedRotarySpringGetRestAngle cpDampedRotarySpringSetRestAngle++{# fun unsafe cpDampedRotarySpringGetStiffness {`Constraint'} -> `Double' #}++{# fun unsafe cpDampedRotarySpringSetStiffness {`Constraint', `Double'} -> `()' #}++-- | Spring stiffness.+dampedRotarySpringStiffness :: Constraint -> StateVar Double+dampedRotarySpringStiffness = mkStateVar cpDampedRotarySpringGetStiffness cpDampedRotarySpringSetStiffness++{# fun unsafe cpDampedRotarySpringGetDamping {`Constraint'} -> `Double' #}++{# fun unsafe cpDampedRotarySpringSetDamping {`Constraint', `Double'} -> `()' #}++-- | Spring damping.+dampedRotarySpringDamping :: Constraint -> StateVar Double+dampedRotarySpringDamping = mkStateVar cpDampedRotarySpringGetDamping cpDampedRotarySpringSetDamping++-- | Create new rotation limiting joint+{# fun unsafe cpRotaryLimitJointNew as rotaryLimitJointNew+ { `Body' -- ^ First body to connect+ , `Body' -- ^ Second body to connect+ , `Double' -- ^ Minimum angle in radians the joint will enforce+ , `Double' -- ^ Maximum angle in radians the joint will enforce+ } -> `Constraint' #}++{# fun unsafe cpRotaryLimitJointGetMin {`Constraint'} -> `Double' #}++{# fun unsafe cpRotaryLimitJointSetMin {`Constraint', `Double'} -> `()' #}++-- | Minimum angle in radians the joint will try to enforce.+rotaryLimitJointMin :: Constraint -> StateVar Double+rotaryLimitJointMin = mkStateVar cpRotaryLimitJointGetMin cpRotaryLimitJointSetMin++{# fun unsafe cpRotaryLimitJointGetMax {`Constraint'} -> `Double' #}++{# fun unsafe cpRotaryLimitJointSetMax {`Constraint', `Double'} -> `()' #}++-- | Maximum angle in radians the joint will try to enforce.+rotaryLimitJointMax :: Constraint -> StateVar Double+rotaryLimitJointMax = mkStateVar cpRotaryLimitJointGetMax cpRotaryLimitJointSetMax++-- | Allocate and initialize a ratchet joint.+{# fun unsafe cpRatchetJointNew as ratchetJointNew+ { `Body' -- ^ First body to connect+ , `Body' -- ^ Second body to connect+ , `Double' -- ^ The initial offset to use when deciding where the ratchet angles are.+ , `Double' -- ^ The distance between “clicks”+ } -> `Constraint' #}++{# fun unsafe cpRatchetJointGetAngle {`Constraint'} -> `Double' #}++{# fun unsafe cpRatchetJointSetAngle {`Constraint', `Double'} -> `()' #}++-- | The angle of the current ratchet tooth.+ratchetJointAngle :: Constraint -> StateVar Double+ratchetJointAngle = mkStateVar cpRatchetJointGetAngle cpRatchetJointSetAngle++{# fun unsafe cpRatchetJointGetPhase {`Constraint'} -> `Double' #}++{# fun unsafe cpRatchetJointSetPhase {`Constraint', `Double'} -> `()' #}++-- | The phase offset of the ratchet.+ratchetJointPhase :: Constraint -> StateVar Double+ratchetJointPhase = mkStateVar cpRatchetJointGetPhase cpRatchetJointSetPhase++{# fun unsafe cpRatchetJointGetRatchet {`Constraint'} -> `Double' #}++{# fun unsafe cpRatchetJointSetRatchet {`Constraint', `Double'} -> `()' #}++-- | The angular distance of each ratchet.+ratchetJointRatchet :: Constraint -> StateVar Double+ratchetJointRatchet = mkStateVar cpRatchetJointGetRatchet cpRatchetJointSetRatchet++-- | Allocate and initialize a gear joint.+{# fun unsafe cpGearJointNew as gearJointNew+ { `Body' -- ^ First body to connect+ , `Body' -- ^ Second body to connect+ , `Double' -- ^ The initial angular offset of the two bodies.+ , `Double' -- ^ Ratio measures in absolute terms+ } -> `Constraint' #}++{# fun unsafe cpGearJointGetPhase {`Constraint'} -> `Double' #}++{# fun unsafe cpGearJointSetPhase {`Constraint', `Double'} -> `()' #}++-- | Phase offset of the ratchet.+gearJointPhase :: Constraint -> StateVar Double+gearJointPhase = mkStateVar cpGearJointGetPhase cpGearJointSetPhase++{# fun unsafe cpGearJointGetRatio {`Constraint'} -> `Double' #}++{# fun unsafe cpGearJointSetRatio {`Constraint', `Double'} -> `()' #}++-- | Ratio of the ratchet+gearJointRatio :: Constraint -> StateVar Double+gearJointRatio = mkStateVar cpGearJointGetRatio cpGearJointSetRatio++-- | Allocate and initialize a simple motor.+{# fun unsafe cpSimpleMotorNew as simpleMotorNew+ { `Body' -- ^ First body to connect+ , `Body' -- ^ Second body to connect+ , `Double' -- ^ The desired relative angular velocity.+ } -> `Constraint' #}++{# fun unsafe cpSimpleMotorGetRate {`Constraint'} -> `Double' #}++{# fun unsafe cpSimpleMotorSetRate {`Constraint', `Double'} -> `()' #}++-- | Ratio of angular velocities.+simpleMotorRate :: Constraint -> StateVar Double+simpleMotorRate = mkStateVar cpSimpleMotorGetRate cpSimpleMotorSetRate
+ src/Chiphunk/Low/Helper.chs view
@@ -0,0 +1,99 @@+-- | Description: Helpers functions mostly for estimating certain measures.+-- Module provides helper function mostly for estimating certain measures.+module Chiphunk.Low.Helper+ ( momentForCircle+ , momentForSegment+ , momentForPoly+ , momentForBox+ , areaForCircle+ , areaForSegment+ , areaForPoly+ , centroidForPoly+ , convexHull+ ) where++import Data.VectorSpace+import Foreign+import System.IO.Unsafe++import Chiphunk.Low.Internal++{# import Chiphunk.Low.Types #}++#include <chipmunk/chipmunk.h>+#include <wrapper.h>++-- | Calculate the moment of inertia for a hollow circle, @r1@ and @r2@ are the inner and outer diameters+-- in no particular order. (A solid circle has an inner diameter of 0)+momentForCircle+ :: Double -- ^ Mass+ -> Double -- ^ r1+ -> Double -- ^ r2+ -> Vect -- ^ Offset+ -> Double+momentForCircle m r1 r2 offs = m * (0.5 * (r1 * r1 + r2 * r2) + magnitudeSq offs)++-- | Calculate the moment of inertia for a line segment. The endpoints @a@ and @b@ are relative to the body.+momentForSegment+ :: Double -- ^ Mass+ -> Vect -- ^ a+ -> Vect -- ^ b+ -> Double -- ^ Thickness+ -> Double+momentForSegment m a b r = m * ((len * len + 4 * r * r) / 12 + magnitudeSq offs)+ where+ offs = lerp a b 0.5+ len = magnitude (b ^-^ a) + 2 * r++-- | Calculate the moment of inertia for a solid polygon shape assuming its center of gravity is at its centroid.+-- The offset is added to each vertex.+{# fun pure unsafe cpMomentForPoly as momentForPoly+ { `Double' -- ^ Mass+ , withList* `[Vect]'& -- ^ Vertexes+ , with* %`Vect' -- ^ Offset+ , `Double' -- ^ Thickness+ } -> `Double' #}++-- | Calculate the moment of inertia for a solid box centered on the body.+momentForBox+ :: Double -- ^ Mass+ -> Double -- ^ Width+ -> Double -- ^ Height+ -> Double+momentForBox m w h = m * (w * w + h * h) / 12++-- | Area of a hollow circle.+areaForCircle+ :: Double -- ^ r1+ -> Double -- ^ r2+ -> Double+areaForCircle r1 r2 = pi * abs (r1 * r1 - r2 * r2)++-- | Area of a beveled segment. (Will always be zero if radius is zero)+areaForSegment+ :: Vect -- ^ One end+ -> Vect -- ^ Other end+ -> Double -- ^ Thickness+ -> Double+areaForSegment v1 v2 r = magnitude (v1 ^-^ v2) * 2 * r + pi * r * r++-- | Signed area of a polygon shape. Returns a negative number for polygons with a clockwise winding.+{# fun pure unsafe cpAreaForPoly as areaForPoly+ { withList* `[Vect]'& -- ^ Vertexes+ , `Double' -- ^ Thickness+ } -> `Double' #}++-- | Calculate the centroid for a polygon.+{# fun pure unsafe w_cpCentroidForPoly as centroidForPoly {withList* `[Vect]'&, alloca- `Vect' peek*} -> `()' #}++-- | Calculate the convex hull of a given set of points.+convexHull+ :: [Vect] -- ^ Set of vertexes+ -> Double -- ^ Allowed amount to shrink the hull when simplifying it. A tolerance of 0 creates an exact hull.+ -> ([Vect], Int) -- ^ Second element is index of first output vertex in input list.+convexHull vs tol = unsafePerformIO $+ withArray vs $ \pVs ->+ allocaArray (length vs) $ \pRes ->+ alloca $ \pFst -> do+ n <- {# call cpConvexHull as c_convexHull #} (fromIntegral $ length vs) pVs pRes pFst (realToFrac tol)+ (,) <$> peekArray (fromIntegral n) pRes <*> (fromIntegral <$> peek pFst)
+ src/Chiphunk/Low/Internal.hs view
@@ -0,0 +1,9 @@+module Chiphunk.Low.Internal where++import Foreign+import Foreign.C.Types++-- | Internal helper function to convert list of storable elements to tuple of length and elements stored in memory+-- and executes IO actions, cleaning up afterwards.+withList :: Storable a => [a] -> ((CInt, Ptr a) -> IO b) -> IO b+withList xs inner = withArray xs $ \p -> inner (fromIntegral $ length xs, castPtr p)
+ src/Chiphunk/Low/Math.hs view
@@ -0,0 +1,32 @@+-- | Description: Math helpers.+-- Module provides mathematics helper functions.+module Chiphunk.Low.Math where++import Data.VectorSpace++-- | Clamp @f@ to be between @min@ and @max@+fClamp+ :: Double -- ^ f+ -> Double -- ^ min+ -> Double -- ^ max+ -> Double+fClamp x a b+ | x < a = a+ | x > b = b+ | otherwise = x++-- | Linearly interpolate between @f1@ and @f2@+fLerp+ :: Double -- ^ f1+ -> Double -- ^ f2+ -> Double+ -> Double+fLerp = lerp++-- | Linearly interpolate from @f1@ towards @f2@ by no more than @d@.+fLerpConst+ :: Double -- ^ f1+ -> Double -- ^ f2+ -> Double -- ^ d+ -> Double+fLerpConst x y d = x + fClamp (y - x) (-d) d
+ src/Chiphunk/Low/Shape.chs view
@@ -0,0 +1,226 @@+-- | Description: Shapes manipulations+-- Module provides access to the shapes which define collisions of rigid bodies.+module Chiphunk.Low.Shape+ ( Shape+ , shapeBody+ , shapeBB+ , shapeSensor+ , shapeElasticity+ , shapeFriction+ , shapeSurfaceVelocity+ , shapeCollisionType+ , ShapeFilter (..)+ , ShapeFilterPtr+ , shapeFilter+ , shapeSpace+ , shapeUserData+ , shapeFree+ , shapeCacheBB+ , shapeUpdate+ , circleShapeNew+ , segmentShapeNew+ , segmentShapeNeighbors+ , polyShapeNew+ , polyShapeNewRaw+ , boxShapeNew+ , boxShapeNew2+ ) where++import Foreign++import Chiphunk.Low.Internal+import Data.StateVar++{# import Chiphunk.Low.Types #}++#include <chipmunk/chipmunk.h>+#include <wrapper.h>++{# fun unsafe cpShapeGetBody {`Shape'} -> `Body' #}++{# fun unsafe cpShapeSetBody {`Shape', `Body'} -> `()' #}++-- | The rigid body the shape is attached to.+-- Can only be set when the shape is not added to a space.+shapeBody :: Shape -> StateVar Body+shapeBody = mkStateVar cpShapeGetBody cpShapeSetBody++{# fun unsafe w_cpShapeGetBB {`Shape', alloca- `BB' peek*} -> `()' #}++-- | The bounding box of the shape.+-- Only guaranteed to be valid after 'shapeCacheBB' or 'spaceStep' is called.+-- Moving a body that a shape is connected to does not update its bounding box.+-- For shapes used for queries that aren’t attached to bodies, you can also use 'shapeUpdate'.+shapeBB :: Shape -> GettableStateVar BB+shapeBB = makeGettableStateVar . w_cpShapeGetBB++{# fun unsafe cpShapeGetSensor {`Shape'} -> `Bool' #}++{# fun unsafe cpShapeSetSensor {`Shape', `Bool'} -> `()' #}++-- | A boolean value if this shape is a sensor or not.+-- Sensors only call collision callbacks, and never generate real collisions.+shapeSensor :: Shape -> StateVar Bool+shapeSensor = mkStateVar cpShapeGetSensor cpShapeSetSensor++{# fun unsafe cpShapeGetElasticity {`Shape'} -> `Double' #}++{# fun unsafe cpShapeSetElasticity {`Shape', `Double'} -> `()' #}++-- | Elasticity of the shape.+-- A value of 0.0 gives no bounce, while a value of 1.0 will give a “perfect” bounce.+-- However due to inaccuracies in the simulation using 1.0 or greater is not recommended however.+--+-- The elasticity for a collision is found by multiplying the elasticity of the individual shapes together.+shapeElasticity :: Shape -> StateVar Double+shapeElasticity = mkStateVar cpShapeGetElasticity cpShapeSetElasticity++{# fun unsafe cpShapeGetFriction {`Shape'} -> `Double' #}++{# fun unsafe cpShapeSetFriction {`Shape', `Double'} -> `()' #}++-- | Friction coefficient.+-- Chipmunk uses the Coulomb friction model, a value of 0.0 is frictionless.+--+-- The friction for a collision is found by multiplying the friction of the individual shapes together.+-- <http://www.roymech.co.uk/Useful_Tables/Tribology/co_of_frict.htm Table of friction coefficients.>+shapeFriction :: Shape -> StateVar Double+shapeFriction = mkStateVar cpShapeGetFriction cpShapeSetFriction++{# fun unsafe w_cpShapeGetSurfaceVelocity {`Shape', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpShapeSetSurfaceVelocity {`Shape', with* %`Vect'} -> `()' #}++-- | The surface velocity of the object.+-- Useful for creating conveyor belts or players that move around.+-- This value is only used when calculating friction, not resolving the collision.+shapeSurfaceVelocity :: Shape -> StateVar Vect+shapeSurfaceVelocity = mkStateVar w_cpShapeGetSurfaceVelocity cpShapeSetSurfaceVelocity++{# fun unsafe cpShapeGetCollisionType {`Shape'} -> `CollisionType' fromIntegral #}++{# fun unsafe cpShapeSetCollisionType {`Shape', fromIntegral `CollisionType'} -> `()' #}++-- | Collision type of this shape.+-- | You can assign types to Chipmunk collision shapes+-- that trigger callbacks when objects of certain types touch.+-- See the callbacks section for more information.+shapeCollisionType :: Shape -> StateVar CollisionType+shapeCollisionType = mkStateVar cpShapeGetCollisionType cpShapeSetCollisionType++-- | Fast collision filtering type that is used to determine if two objects collide+-- before calling collision or query callbacks.+data ShapeFilter = ShapeFilter+ { sfGroup :: !WordPtr+ , sfCategories :: !Word32+ , sfMask :: !Word32+ } deriving Show++instance Storable ShapeFilter where+ sizeOf _ = {# sizeof cpShapeFilter #}+ alignment _ = {# alignof cpShapeFilter #}+ poke p (ShapeFilter g c m) = do+ {# set cpShapeFilter->group #} p $ fromIntegral g+ {# set cpShapeFilter->categories #} p $ fromIntegral c+ {# set cpShapeFilter->mask #} p $ fromIntegral m+ peek p = ShapeFilter <$> (fromIntegral <$> {# get cpShapeFilter->group #} p)+ <*> (fromIntegral <$> {# get cpShapeFilter->categories #} p)+ <*> (fromIntegral <$> {# get cpShapeFilter->mask #} p)++-- | Pointer to 'ShapeFilter'+{# pointer *cpShapeFilter as ShapeFilterPtr -> ShapeFilter #}++{# fun unsafe w_cpShapeGetFilter {`Shape', alloca- `ShapeFilter' peek*} -> `()' #}++{# fun unsafe cpShapeSetFilter {`Shape', with* %`ShapeFilter'} -> `()' #}++-- | The collision filter for this shape. See Filtering Collisions for more information.+shapeFilter :: Shape -> StateVar ShapeFilter+shapeFilter = mkStateVar w_cpShapeGetFilter cpShapeSetFilter++{# fun unsafe cpShapeGetSpace {`Shape'} -> `Space' #}++-- | The 'Space' that @shape@ has been added to.+shapeSpace :: Shape -> GettableStateVar Space+shapeSpace = makeGettableStateVar . cpShapeGetSpace++{# fun unsafe cpShapeGetUserData {`Shape'} -> `DataPtr' #}++{# fun unsafe cpShapeSetUserData {`Shape', `DataPtr'} -> `()' #}++-- | A user definable data pointer.+-- If you set this to point at the game object the shapes is for,+-- then you can access your game object from Chipmunk callbacks.+shapeUserData :: Shape -> StateVar DataPtr+shapeUserData = mkStateVar cpShapeGetUserData cpShapeSetUserData++-- | Deallocates shape.+{# fun cpShapeFree as shapeFree {`Shape'} -> `()' #}+-- no "unsafe" qualifier because I think it may trigger separte callbacks++-- | Synchronizes @shape@ with the body its attached to.+{# fun unsafe w_cpShapeCacheBB as shapeCacheBB+ { `Shape' -- ^ shape+ , alloca- `BB' peek*+ } -> `()' #}++-- | Sets the position and rotation of the shape+{# fun unsafe w_cpShapeUpdate as shapeUpdate+ { `Shape' -- ^ @shape@+ , with* %`Transform'+ , alloca- `BB' peek*+ } -> `()' #}++-- | Create new circle-like shape.+{# fun unsafe cpCircleShapeNew as circleShapeNew+ { `Body' -- ^ The body to attach the circle to.+ , `Double' -- ^ Radius of the circle.+ , with* %`Vect' -- ^ Offset from the body's center of gravity in body local coordinates.+ } -> `Shape' #}++-- | Create new segment-shaped shape.+{# fun unsafe cpSegmentShapeNew as segmentShapeNew+ { `Body' -- ^ The body to attach the segment to.+ , with* %`Vect' -- ^ One endpoint.+ , with* %`Vect' -- ^ Another endpoint.+ , `Double' -- ^ The thickness of the segment.+ } -> `Shape' #}++{# fun unsafe cpSegmentShapeSetNeighbors {`Shape', with* %`Vect', with* %`Vect'} -> `()' #}++-- | When you have a number of segment shapes that are all joined together,+-- things can still collide with the “cracks” between the segments.+-- By setting the neighbor segment endpoints+-- you can tell Chipmunk to avoid colliding with the inner parts of the crack.+segmentShapeNeighbors :: Shape -> SettableStateVar (Vect, Vect)+segmentShapeNeighbors shape =+ makeSettableStateVar $ \(v1, v2) ->+ cpSegmentShapeSetNeighbors shape v1 v2++-- | A convex hull will be calculated from the vertexes automatically.+-- The polygon shape will be created with a radius, increasing the size of the shape.+{# fun unsafe cpPolyShapeNew as polyShapeNew+ { `Body' -- ^ The body to attach the poly to.+ , withList* `[Vect]'& -- ^ The array of 'Vect' structs.+ , with* %`Transform' -- ^ The transform that will be applied to every vertex.+ , `Double' -- ^ Radius.+ } -> `Shape' #}++-- | Alternate constructors for poly shapes. This version does not apply a transform nor does it create a convex hull.+-- Verticies must be provided with a counter-clockwise winding.+{# fun unsafe cpPolyShapeNewRaw as polyShapeNewRaw {`Body', withList* `[Vect]'&, `Double'} -> `Shape' #}++-- | Createa box shape from dimensions.+{# fun unsafe cpBoxShapeNew as boxShapeNew+ { `Body' -- ^ The body to attach to+ , `Double' -- ^ Box width+ , `Double' -- ^ Box height+ , `Double' -- ^ Radius+ } -> `Shape' #}++-- | Alternative to 'boxShapeNew' using 'BB' to set size.+{# fun unsafe cpBoxShapeNew2 as boxShapeNew2+ { `Body' -- ^ The body to attach to+ , with* %`BB' -- ^ Shape size+ , `Double' -- ^ Radius+ } -> `Shape' #}
+ src/Chiphunk/Low/Space.chs view
@@ -0,0 +1,266 @@+-- | Description: Manipulate space+-- Module defined utilities for manipulating spaces.+module Chiphunk.Low.Space+ ( Space+ , spaceIterations+ , spaceGravity+ , spaceDamping+ , spaceIdleSpeedThreshold+ , spaceSleepTimeThreshold+ , spaceCollisionSlop+ , spaceCollisionBias+ , spaceCollisionPersistence+ , spaceCurrentTimeStep+ , spaceIsLocked+ , spaceUserData+ , spaceStaticBody+ , spaceNew+ , spaceFree+ , spaceAddShape+ , spaceAddBody+ , spaceAddConstraint+ , spaceRemoveShape+ , spaceRemoveBody+ , spaceRemoveConstraint+ , spaceContainsShape+ , spaceContainsBody+ , spaceContainsConstraint+ , spaceReindexShape+ , spaceReindexShapesForBody+ , spaceReindexStatic+ , SpaceBodyIteratorFunc+ , spaceEachBody+ , SpaceShapeIteratorFunc+ , spaceEachShape+ , SpaceConstraintIteratorFunc+ , spaceEachConstraint+ , spaceStep+ ) where++import Control.Exception.Safe+import Data.StateVar+import Foreign++import Chiphunk.Low.Vect+{# import Chiphunk.Low.Types #}++#include <chipmunk/chipmunk.h>+#include <wrapper.h>++{# fun unsafe cpSpaceGetIterations {`Space'} -> `Int' #}++{# fun unsafe cpSpaceSetIterations {`Space', `Int'} -> `()' #}++-- | Iterations allow you to control the accuracy of the solver.+-- Defaults to 10. See above for more information.+spaceIterations :: Space -> StateVar Int+spaceIterations = mkStateVar cpSpaceGetIterations cpSpaceSetIterations++{# fun unsafe w_cpSpaceGetGravity {`Space', alloca- `Vect' peek*} -> `()' #}++{# fun unsafe cpSpaceSetGravity {`Space', with* %`Vect'} -> `()' #}++-- | Global gravity applied to the space. Defaults to 'vZero'.+-- Can be overridden on a per body basis by writing custom integration functions.+-- Changing the gravity will activate all sleeping bodies in the space.+spaceGravity :: Space -> StateVar Vect+spaceGravity = mkStateVar w_cpSpaceGetGravity cpSpaceSetGravity++{# fun unsafe cpSpaceGetDamping {`Space'} -> `Double' #}++{# fun unsafe cpSpaceSetDamping {`Space', `Double'} -> `()' #}++-- | Amount of simple damping to apply to the space.+-- A value of 0.9 means that each body will lose 10% of its velocity per second.+-- Defaults to 1. Like gravity, it can be overridden on a per body basis.+spaceDamping :: Space -> StateVar Double+spaceDamping = mkStateVar cpSpaceGetDamping cpSpaceSetDamping++{# fun unsafe cpSpaceGetIdleSpeedThreshold {`Space'} -> `Double' #}++{# fun unsafe cpSpaceSetIdleSpeedThreshold {`Space', `Double'} -> `()' #}++-- | Speed threshold for a body to be considered idle.+-- The default value of 0 means the space estimates a good threshold based on gravity.+spaceIdleSpeedThreshold :: Space -> StateVar Double+spaceIdleSpeedThreshold = mkStateVar cpSpaceGetIdleSpeedThreshold cpSpaceSetIdleSpeedThreshold++{# fun unsafe cpSpaceGetSleepTimeThreshold {`Space'} -> `Double' #}++{# fun unsafe cpSpaceSetSleepTimeThreshold {`Space', `Double'} -> `()' #}++-- | Time a group of bodies must remain idle in order to fall asleep.+-- The default value of INFINITY disables the sleeping feature.+spaceSleepTimeThreshold :: Space -> StateVar Double+spaceSleepTimeThreshold = mkStateVar cpSpaceGetSleepTimeThreshold cpSpaceSetSleepTimeThreshold++{# fun unsafe cpSpaceGetCollisionSlop {`Space'} -> `Double' #}++{# fun unsafe cpSpaceSetCollisionSlop {`Space', `Double'} -> `()' #}++-- | Amount of overlap between shapes that is allowed.+-- To improve stability, set this as high as you can without noticable overlapping.+-- It defaults to @0.1@.+spaceCollisionSlop :: Space -> StateVar Double+spaceCollisionSlop = mkStateVar cpSpaceGetCollisionSlop cpSpaceSetCollisionSlop++{# fun unsafe cpSpaceGetCollisionBias {`Space'} -> `Double' #}++{# fun unsafe cpSpaceSetCollisionBias {`Space', `Double'} -> `()' #}++-- | Chipmunk allows fast moving objects to overlap, then fixes the overlap over time.+-- Overlapping objects are unavoidable even if swept collisions are supported,+-- and this is an efficient and stable way to deal with overlapping objects.+-- The bias value controls what percentage of overlap remains unfixed+-- after a second and defaults to ~0.2%.+--+-- Valid values are in the range from 0 to 1,+-- but using 0 is not recommended for stability reasons.+--+-- The default value is calculated as @(1.0 - 0.1) ^ 60@+-- meaning that Chipmunk attempts to correct 10% of error ever 1/60th of a second.+--+-- __Note__: Very very few games will need to change this value.+spaceCollisionBias :: Space -> StateVar Double+spaceCollisionBias = mkStateVar cpSpaceGetCollisionBias cpSpaceSetCollisionBias++{# fun unsafe cpSpaceGetCollisionPersistence {`Space'} -> `Word32' #}++{# fun unsafe cpSpaceSetCollisionPersistence {`Space', `Word32'} -> `()' #}++-- | The number of frames the space keeps collision solutions around for.+-- Helps prevent jittering contacts from getting worse.+-- This defaults to 3 and very very very few games will need to change this value.+spaceCollisionPersistence :: Space -> StateVar Word32+spaceCollisionPersistence = mkStateVar cpSpaceGetCollisionPersistence cpSpaceSetCollisionPersistence++{# fun unsafe cpSpaceGetCurrentTimeStep {`Space'} -> `Double' #}++-- | The current (if you are in a callback from 'spaceStep')+-- or most recent (outside of a 'spaceStep' call) timestep.+spaceCurrentTimeStep :: Space -> GettableStateVar Double+spaceCurrentTimeStep = makeGettableStateVar . cpSpaceGetCurrentTimeStep++-- | Returns true when you cannot add/remove objects from the space.+-- In particular, spaces are locked when in a collision callback.+-- Instead, run your code in a post-step callback instead.+{# fun unsafe cpSpaceIsLocked as spaceIsLocked {`Space'} -> `Bool' #}++{# fun unsafe cpSpaceGetUserData {`Space'} -> `DataPtr' #}++{# fun unsafe cpSpaceSetUserData {`Space', `DataPtr'} -> `()' #}++-- | A user definable data pointer.+-- It is often useful to point this at the gamestate object+-- or scene management object that owns the space.+spaceUserData :: Space -> StateVar DataPtr+spaceUserData = mkStateVar cpSpaceGetUserData cpSpaceSetUserData++{# fun unsafe cpSpaceGetStaticBody {`Space'} -> `Body' #}++-- | A dedicated static body for the space.+-- You don’t have to use it,+-- but because its memory is managed automatically with the space its very convenient.+-- You can set its user data pointer to something helpful if you want for callbacks.+spaceStaticBody :: Space -> GettableStateVar Body+spaceStaticBody = makeGettableStateVar . cpSpaceGetStaticBody++-- | Standard Chipmunk allocation function.+{# fun unsafe cpSpaceNew as spaceNew {} -> `Space' #}++-- | Standard Chipmunk deallocation function.+{# fun cpSpaceFree as spaceFree {`Space'} -> `()' #}+-- no "unsafe" qualifier because I think it may trigger separte callbacks++-- | Add shape to the space.+{# fun unsafe cpSpaceAddShape as spaceAddShape {`Space', `Shape'} -> `()' #}++-- | Add body to the space.+{# fun unsafe cpSpaceAddBody as spaceAddBody {`Space', `Body'} -> `()' #}++-- | Add constraint to the space.+{# fun unsafe cpSpaceAddConstraint as spaceAddConstraint {`Space', `Constraint'} -> `()' #}++-- | Remove shape from the space.+{# fun cpSpaceRemoveShape as spaceRemoveShape {`Space', `Shape'} -> `()' #}+-- no "unsafe" qualifier because I think it may trigger separte callbacks++-- | Remove body from the space.+{# fun cpSpaceRemoveBody as spaceRemoveBody {`Space', `Body'} -> `()' #}+-- no "unsafe" qualifier because I think it may trigger separte callbacks++-- | Remove constraint from the space.+{# fun cpSpaceRemoveConstraint as spaceRemoveConstraint {`Space', `Constraint'} -> `()' #}+-- no "unsafe" qualifier because I think it may trigger separte callbacks++-- | Check if shape is attached to the space.+{# fun unsafe cpSpaceContainsShape as spaceContainsShape {`Space', `Shape'} -> `Bool' #}++-- | Check if body is attached to the space.+{# fun unsafe cpSpaceContainsBody as spaceContainsBody {`Space', `Body'} -> `Bool' #}++-- | Check if constraint is attached to the space.+{# fun unsafe cpSpaceContainsConstraint as spaceContainsConstraint {`Space', `Constraint'} -> `Bool' #}++-- | Reindex a specific shape.+{# fun unsafe cpSpaceReindexShape as spaceReindexShape {`Space', `Shape'} -> `()' #}++-- | Reindex all the shapes for a certain body.+{# fun unsafe cpSpaceReindexShapesForBody as spaceReindexShapesForBody {`Space', `Body'} -> `()' #}++-- | Reindex all static shapes. Generally updating only the shapes that changed is faster.+{# fun unsafe cpSpaceReindexStatic as spaceReindexStatic {`Space'} -> `()' #}++-- | Type of callback which can be used to iterate all 'Body's in a 'Space'.+type SpaceBodyIteratorFunc = Body -> Ptr () -> IO ()++foreign import ccall unsafe "wrapper"+ mkSpaceBodyIteratorFunc :: SpaceBodyIteratorFunc -> IO (FunPtr SpaceBodyIteratorFunc)++-- | Call @func@ for each body in the @space@ also passing along your @data@ pointer.+-- Sleeping bodies are included, but static and kinematic bodies are not as they aren’t added to the space.+{# fun cpSpaceEachBody as spaceEachBody+ { `Space' -- ^ space+ , withIterator* `SpaceBodyIteratorFunc' -- ^ func+ , `Ptr ()' -- ^ data+ } -> `()' #}+ where+ withIterator i = mkSpaceBodyIteratorFunc i `bracket` freeHaskellFunPtr++-- | Type of callback which can be used to iterate all 'Shape's in a 'Space'.+type SpaceShapeIteratorFunc = Shape -> Ptr () -> IO ()++foreign import ccall unsafe "wrapper"+ mkSpaceShapeIteratorFunc :: SpaceShapeIteratorFunc -> IO (FunPtr SpaceShapeIteratorFunc)++-- | Call @func@ for each shape in the @space@ also passing along your @data@ pointer.+-- Sleeping and static shapes are included.+{# fun cpSpaceEachShape as spaceEachShape+ { `Space' -- ^ space+ , withIterator* `SpaceShapeIteratorFunc' -- ^ func+ , `Ptr ()' -- ^ data+ } -> `()' #}+ where+ withIterator i = mkSpaceShapeIteratorFunc i `bracket` freeHaskellFunPtr++-- | Type of callback which can be used to iterate all 'Constraint's in a 'Space'.+type SpaceConstraintIteratorFunc = Constraint -> Ptr () -> IO ()++foreign import ccall unsafe "wrapper"+ mkSpaceConstraintIteratorFunc :: SpaceConstraintIteratorFunc -> IO (FunPtr SpaceConstraintIteratorFunc)++-- | Call func for each constraint in the space also passing along your data pointer.+{# fun cpSpaceEachConstraint as spaceEachConstraint+ { `Space' -- ^ space+ , withIterator* `SpaceConstraintIteratorFunc' -- ^ func+ , `Ptr ()' -- ^ data+ } -> `()' #}+ where+ withIterator i = mkSpaceConstraintIteratorFunc i `bracket` freeHaskellFunPtr++-- | Update the space for the given time step. Using a fixed time step is highly recommended.+-- Doing so can greatly increase the quality of the simulation.+-- The easiest way to do constant timesteps is to simple step forward by 1/60th of a second+-- (or whatever your target framerate is) for each frame regardless of how long it took to render.+-- This works fine for many games, but a better way to do it is to separate your physics timestep and rendering.+{# fun cpSpaceStep as spaceStep {`Space', `Double'} -> `()' #}
+ src/Chiphunk/Low/Types.chs view
@@ -0,0 +1,224 @@+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeFamilies #-}++-- | Description: Basic Chipmunk data types.+-- Module provides basic Chipmunk data types.+module Chiphunk.Low.Types+ ( Vect (..)+ , VectPtr+ , BB (..)+ , BBPtr+ , DataPtr+ , Body (..)+ , BodyType (..)+ , Space (..)+ , Shape (..)+ , Constraint (..)+ , Arbiter (..)+ , Transform (..)+ , TransformPtr+ , CollisionType+ , CPBool+ , mkStateVar+ ) where++import Data.Cross+import Data.StateVar+import Data.VectorSpace+import Foreign++#include <chipmunk/chipmunk.h>++-- | 2D vector packed into a struct.+data Vect = Vect+ { vX :: !Double, vY :: !Double+ } deriving (Eq, Show)++instance AdditiveGroup Vect where+ zeroV = Vect 0 0+ negateV (Vect x y) = Vect (-x) (-y)+ Vect x1 y1 ^+^ Vect x2 y2 = Vect (x1 + x2) (y1 + y2)+ Vect x1 y1 ^-^ Vect x2 y2 = Vect (x1 - x2) (y1 - y2)++instance VectorSpace Vect where+ type Scalar Vect = Double+ f *^ Vect x y = Vect (f * x) (f * y)++instance InnerSpace Vect where+ Vect x1 y1 <.> Vect x2 y2 = x1 * x2 + y1 * y2++instance HasCross2 Vect where+ cross2 (Vect x y) = Vect (-y) x++instance Storable Vect where+ sizeOf _ = {# sizeof cpVect #}+ alignment _ = {# alignof cpVect #}+ poke p (Vect x y) = do+ {# set cpVect->x #} p $ realToFrac x+ {# set cpVect->y #} p $ realToFrac y+ peek p = Vect <$> (realToFrac <$> {# get cpVect->x #} p)+ <*> (realToFrac <$> {# get cpVect->y #} p)++-- | Pointer to vector.+{# pointer *cpVect as VectPtr -> Vect #}++-- | Simple bounding box struct. Stored as left, bottom, right, top values.+data BB = BB+ { bbL :: !Double, bbB :: !Double, bbR :: !Double, bbT :: !Double+ } deriving (Show)++instance Storable BB where+ sizeOf _ = {# sizeof cpBB #}+ alignment _ = {# alignof cpBB #}+ poke p (BB l b r t) = do+ {# set cpBB->l #} p $ realToFrac l+ {# set cpBB->b #} p $ realToFrac b+ {# set cpBB->r #} p $ realToFrac r+ {# set cpBB->t #} p $ realToFrac t+ peek p = BB <$> (realToFrac <$> {# get cpBB->l #} p)+ <*> (realToFrac <$> {# get cpBB->b #} p)+ <*> (realToFrac <$> {# get cpBB->r #} p)+ <*> (realToFrac <$> {# get cpBB->t #} p)++-- | Pointer to bounding box.+{# pointer *cpBB as BBPtr -> BB #}++-- | Pointer to user data.+{# pointer cpDataPointer as DataPtr #}++-- | Rigid body somewhere in C code.+{# pointer *cpBody as Body newtype #}++instance Storable Body where+ sizeOf (Body p) = sizeOf p+ alignment (Body p) = alignment p+ poke p (Body b) = poke (castPtr p) b+ peek p = Body <$> peek (castPtr p)++-- | Chipmunk supports three different types of bodies with unique behavioral and performance characteristics.+data BodyType =+ BodyTypeDynamic+ -- ^ Dynamic bodies are the default body type.+ -- They react to collisions, are affected by forces and gravity, and have a finite amount of mass.+ -- These are the type of bodies that you want the physics engine to simulate for you.+ -- Dynamic bodies interact with all types of bodies and can generate collision callbacks.+ | BodyTypeKimenatic+ -- ^ Kinematic bodies are bodies that are controlled from your code instead of inside the physics engine.+ -- They arent affected by gravity+ -- and they have an infinite amount of mass so they don’t react to collisions or forces with other bodies.+ -- Kinematic bodies are controlled by setting their velocity, which will cause them to move.+ -- Good examples of kinematic bodies might include things like moving platforms.+ -- Objects that are touching or jointed to a kinematic body are never allowed to fall asleep.+ | BodyTypeStatic+ -- ^ Static bodies are bodies that never (or rarely) move.+ -- Using static bodies for things like terrain offers a big performance boost over other body types —+ -- because Chipmunk doesn’t need to check for collisions between static objects+ -- and it never needs to update their collision information.+ -- Additionally, because static bodies don’t move,+ -- Chipmunk knows it’s safe to let objects that are touching or jointed to them fall asleep.+ -- Generally all of your level geometry will be attached to a static body+ -- except for things like moving platforms or doors.+ -- Every space provide a built-in static body for your convenience.+ -- Static bodies can be moved, but there is a performance penalty as the collision information is recalculated.+ -- There is no penalty for having multiple static bodies, and it can be useful for simplifying your code+ -- by allowing different parts of your static geometry to be initialized or moved separately.++{# enum cpBodyType as BodyType nocode+ { CP_BODY_TYPE_DYNAMIC as BodyTypeDynamic+ , CP_BODY_TYPE_KINEMATIC as BodyTypeKimenatic+ , CP_BODY_TYPE_STATIC as BodyTypeStatic+ } #}++deriving instance Show BodyType++-- | Spaces in Chipmunk are the basic unit of simulation. You add rigid bodies, shapes, and constraints to the space+-- and then step them all forward through time together.+{# pointer *cpSpace as Space newtype #}++instance Storable Space where+ sizeOf (Space p) = sizeOf p+ alignment (Space p) = alignment p+ poke p (Space b) = poke (castPtr p) b+ peek p = Space <$> peek (castPtr p)++-- | There are currently 3 collision shape types:+--+-- * __Circles__: Fastest and simplest collision shape.+--+-- * __Line segments__: Meant mainly as a static shape. Can be beveled in order to give them a thickness.+--+-- * __Convex polygons__: Slowest, but most flexible collision shape.+--+-- You can add as many shapes to a body as you wish. That is why the two types are separate.+--+-- Combining multiple shapes gives you the flexibility to make any object you want+-- as well as providing different areas of the same object with different friction, elasticity or callback values.+{# pointer *cpShape as Shape newtype #}++instance Storable Shape where+ sizeOf (Shape p) = sizeOf p+ alignment (Shape p) = alignment p+ poke p (Shape b) = poke (castPtr p) b+ peek p = Shape <$> peek (castPtr p)++-- | A constraint is something that describes how two bodies interact with each other. (how they constrain each other)+-- Constraints can be simple joints that allow bodies to pivot around each other like the bones in your body,+-- or they can be more abstract like the gear joint or motors.+{# pointer *cpConstraint as Constraint newtype #}++instance Storable Constraint where+ sizeOf (Constraint p) = sizeOf p+ alignment (Constraint p) = alignment p+ poke p (Constraint b) = poke (castPtr p) b+ peek p = Constraint <$> peek (castPtr p)++-- | Chipmunk’s 'Arbiter' struct encapsulates a pair of colliding shapes and all of the data about their collision.+-- 'Arbiter' is created when a collision starts, and persist until those shapes are no longer colliding.+--+-- Why are they called arbiters? The short answer is that I kept using the word “arbitrates”+-- to describe the way that collisions were resolved and then I saw that Box2D actually called them arbiters+-- way back in 2006 when I was looking at its solver.+-- An arbiter is like a judge, a person that has authority to settle disputes between two people.+-- It was a fun, fitting name and was shorter to type than CollisionPair which I had been using.+-- It was originally meant to be a private internal structure only, but evolved to be useful from callbacks.+{# pointer *cpArbiter as Arbiter newtype #}++instance Storable Arbiter where+ sizeOf (Arbiter p) = sizeOf p+ alignment (Arbiter p) = alignment p+ poke p (Arbiter b) = poke (castPtr p) b+ peek p = Arbiter <$> peek (castPtr p)++-- | Type used for 2×3 affine transforms in Chipmunk.+data Transform = Transform+ { tA :: !Double, tB :: !Double, tC :: !Double, tD :: !Double, tTx :: !Double, tTy :: !Double+ } deriving Show++instance Storable Transform where+ sizeOf _ = {# sizeof cpTransform #}+ alignment _ = {# alignof cpTransform #}+ poke p (Transform a b c d tx ty) = do+ {# set cpTransform->a #} p $ realToFrac a+ {# set cpTransform->b #} p $ realToFrac b+ {# set cpTransform->c #} p $ realToFrac c+ {# set cpTransform->d #} p $ realToFrac d+ {# set cpTransform->tx #} p $ realToFrac tx+ {# set cpTransform->ty #} p $ realToFrac ty+ peek p = Transform <$> (realToFrac <$> {# get cpTransform->a #} p)+ <*> (realToFrac <$> {# get cpTransform->b #} p)+ <*> (realToFrac <$> {# get cpTransform->c #} p)+ <*> (realToFrac <$> {# get cpTransform->d #} p)+ <*> (realToFrac <$> {# get cpTransform->tx #} p)+ <*> (realToFrac <$> {# get cpTransform->ty #} p)++-- | Pointer to 'Transform'+{# pointer *cpTransform as TransformPtr -> Transform #}++-- | Collision type+type CollisionType = WordPtr++type CPBool = {# type cpBool #}++-- | 'makeStateVar' lifted to reader monad+mkStateVar :: (a -> IO b) -> (a -> b -> IO ()) -> a -> StateVar b+mkStateVar g s i = makeStateVar (g i) (s i)
+ src/Chiphunk/Low/Vect.chs view
@@ -0,0 +1,219 @@+-- | Description: 2D vector manipulations.+-- Module provides utilities to manipulate 2D vectors. Most of the code is re-implemented in Haskell (mirrorring C)+-- to avoid unnecessary foreign calls/marshalling, but only for simple operations.+--+-- Also note that 'Vect' has 'Eq', 'AdditiveGroup', 'VectorSpace', 'InnerSpace' and 'HasCross2' instances.+-- Large part of functions in this module just provides Chiphunk-compatible aliases for those instances' methods.+module Chiphunk.Low.Vect+ ( Vect (..)+ , cpv+ , vZero+ , vEql+ , vAdd+ , vSub+ , vNeg+ , vMult+ , vDot+ , vCross+ , vPerp+ , vRPerp+ , vProject+ , vRotate+ , vUnRotate+ , vLength+ , vLengthSq+ , vLerp+ , vLerpConst+ , vSLerp+ , vSLerpConst+ , vNormalize+ , vClamp+ , vDist+ , vDistSq+ , vNear+ , vForAngle+ , vToAngle+ ) where++import Data.Cross+import Data.VectorSpace+import Foreign++{# import Chiphunk.Low.Types #}++#include <wrapper.h>++-- | Convenience constructor for creating new cpVect structs.+-- Alias for 'Vect'+cpv :: Double -> Double -> Vect+cpv = Vect++-- | Constant for the zero vector.+--+-- Alias for 'zeroV'+vZero :: Vect+vZero = zeroV++-- | Check if two vectors are equal. (Be careful when comparing floating point numbers!)+--+-- Alias for '=='.+vEql :: Vect -> Vect -> Bool+vEql = (==)++-- | Add two vectors.+--+-- Alias for '^+^'.+vAdd :: Vect -> Vect -> Vect+vAdd = (^+^)++-- | Subtract two vectors.+--+-- Alias for '^-^'.+vSub :: Vect -> Vect -> Vect+vSub = (^-^)++-- | Negate a vector.+--+-- Alias for 'negateV'.+vNeg :: Vect -> Vect+vNeg = negateV++-- | Scalar multiplication.+--+-- Alias for '^*'.+vMult :: Vect -> Double -> Vect+vMult = (^*)++-- | Vector dot product.+--+-- Alias for '<.>'.+vDot :: Vect -> Vect -> Double+vDot = (<.>)++-- | 2D vector cross product analog. The cross product of 2D vectors results in a 3D vector with only a z component.+-- This function returns the value along the z-axis.+vCross :: Vect -> Vect -> Double+Vect x1 y1 `vCross` Vect x2 y2 = x1 * y2 - y1 * x2++-- | Returns a perpendicular vector. (90 degree rotation)+--+-- Alias for 'cross2'.+vPerp :: Vect -> Vect+vPerp = cross2++-- | Returns a perpendicular vector. (-90 degree rotation)+vRPerp :: Vect -> Vect+vRPerp v = negateV $ cross2 v++-- | Returns the vector projection of @v1@ onto @v2@.+--+-- Alias for 'project'.+vProject+ :: Vect -- ^ v1+ -> Vect -- ^ v2+ -> Vect+vProject = project++-- | Uses complex multiplication to rotate @v1@ by @v2@. Scaling will occur if @v1@ is not a unit vector.+vRotate+ :: Vect -- ^ v1+ -> Vect -- ^ v2+ -> Vect+Vect x1 y1 `vRotate` Vect x2 y2 = Vect (x1 * x2 - y1 * y2) (x1 * y2 + x2 * y1)++-- | Inverse of 'vRotate'.+vUnRotate :: Vect -> Vect -> Vect+Vect x1 y1 `vUnRotate` Vect x2 y2 = Vect (x1 * x2 + y1 * y2) (x2 * y1 - x1 * y2)++-- | Returns the length of v.+--+-- Alias for 'magnitude'.+vLength :: Vect -> Double+vLength = magnitude++-- | Returns the squared length of @v@. Faster than 'vLength' when you only need to compare lengths.+--+-- Alias for 'magnitudeSq'.+vLengthSq :: Vect -> Double+vLengthSq = magnitudeSq++-- | Linearly interpolate between @v1@ and @v2@.+--+-- Alias for 'lerp'.+vLerp+ :: Vect -- ^ v1+ -> Vect -- ^ v2+ -> Double+ -> Vect+vLerp = lerp++-- | Linearly interpolate between @v1@ towards @v2@ by distance @d@.+vLerpConst+ :: Vect -- ^ v1+ -> Vect -- ^ v2+ -> Double -- ^ d+ -> Vect+vLerpConst a b l = a ^+^ vClamp (b ^-^ a) l++-- | Spherical linearly interpolate between v1 and v2.+{# fun pure unsafe w_cpvslerp as vSLerp+ { with* %`Vect' -- ^ v1+ , with* %`Vect' -- ^ v2+ , `Double'+ , alloca- `Vect' peek*+ } -> `()' #}++-- | Spherical linearly interpolate between @v1@ towards @v2@ by no more than angle @a@ in radians.+{# fun pure unsafe w_cpvslerpconst as vSLerpConst+ { with* %`Vect' -- ^ v1+ , with* %`Vect' -- ^ v2+ , `Double' -- ^ a+ , alloca- `Vect' peek*+ } -> `()' #}++-- | Returns a normalized copy of @v@. As a special case, it returns 'vZero' when called on 'vZero'.+--+-- Alias for 'normalized'.+vNormalize :: Vect -> Vect+vNormalize = normalized++-- | Clamp @v@ to length @len@.+vClamp+ :: Vect -- ^ v+ -> Double -- ^ len+ -> Vect+vClamp v l+ | magnitudeSq v > l * l = l *^ normalized v+ | otherwise = v++-- | Returns the distance between @v1@ and @v2@.+vDist+ :: Vect -- ^ v1+ -> Vect -- ^ v2+ -> Double+vDist v1 v2 = magnitude $ v1 ^-^ v2++-- | Returns the squared distance between @v1@ and @v2@. Faster than 'vDist' when you only need to compare distances.+vDistSq+ :: Vect -- ^ v1+ -> Vect -- ^ v2+ -> Double+vDistSq v1 v2 = magnitudeSq $ v1 ^-^ v2++-- | Returns true if the distance between @v1@ and @v2@ is less than @dist@.+vNear+ :: Vect -- ^ v1+ -> Vect -- ^ v2+ -> Double -- ^ dist+ -> Bool+vNear v1 v2 d = vDistSq v1 v2 < d * d++-- | Returns the unit length vector for the given angle (in radians).+vForAngle :: Double -> Vect+vForAngle alpha = Vect (cos alpha) (sin alpha)++-- | Returns the angular direction @v@ is pointing in (in radians).+vToAngle+ :: Vect -- ^ v+ -> Double+vToAngle (Vect x y) = atan2 y x
+ src/Chiphunk/wrapper.c view
@@ -0,0 +1,176 @@+#include <wrapper.h>++void w_cpvslerp(cpVect v1, cpVect v2, cpFloat t, cpVect *out)+{+ *out = cpvslerp(v1, v2, t);+}++void w_cpvslerpconst(cpVect v1, cpVect v2, cpFloat a, cpVect *out)+{+ *out = cpvslerpconst(v1, v2, a);+}++void w_cpBodyGetPosition(const cpBody *body, cpVect *out)+{+ *out = cpBodyGetPosition(body);+}++void w_cpBodyGetCenterOfGravity(const cpBody *body, cpVect *out)+{+ *out = cpBodyGetCenterOfGravity(body);+}++void w_cpBodyGetVelocity(const cpBody *body, cpVect *out)+{+ *out = cpBodyGetVelocity(body);+}++void w_cpBodyGetForce(const cpBody *body, cpVect *out)+{+ *out = cpBodyGetForce(body);+}++void w_cpBodyGetRotation(const cpBody *body, cpVect *out)+{+ *out = cpBodyGetRotation(body);+}++void w_cpBodyLocalToWorld(const cpBody *body, cpVect point, cpVect *out)+{+ *out = cpBodyLocalToWorld(body, point);+}++void w_cpBodyWorldToLocal(const cpBody *body, cpVect point, cpVect *out)+{+ *out = cpBodyWorldToLocal(body, point);+}++void w_cpBodyGetVelocityAtWorldPoint(const cpBody *body, cpVect point, cpVect *out)+{+ *out = cpBodyGetVelocityAtWorldPoint(body, point);+}++void w_cpBodyGetVelocityAtLocalPoint(const cpBody *body, cpVect point, cpVect *out)+{+ *out = cpBodyGetVelocityAtLocalPoint(body, point);+}++void w_cpShapeGetBB(const cpShape *shape, cpBB *out)+{+ *out = cpShapeGetBB(shape);+}++void w_cpShapeGetSurfaceVelocity(const cpShape *shape, cpVect *out)+{+ *out = cpShapeGetSurfaceVelocity(shape);+}++void w_cpShapeGetFilter(const cpShape *shape, cpShapeFilter *out)+{+ *out = cpShapeGetFilter(shape);+}++void w_cpShapeCacheBB(cpShape *shape, cpBB *out)+{+ *out = cpShapeCacheBB(shape);+}++void w_cpShapeUpdate(cpShape *shape, cpTransform transform, cpBB *out)+{+ *out = cpShapeUpdate(shape, transform);+}++void w_cpCentroidForPoly(int count, const cpVect *vects, cpVect *out)+{+ *out = cpCentroidForPoly(count, vects);+}++cpFloat w_cpBBSegmentQuery(cpBB bb, cpVect a, cpVect b)+{+ return cpBBSegmentQuery(bb, a, b);+}++cpBool w_cpBBIntersectsSegment(cpBB bb, cpVect a, cpVect b)+{+ return cpBBIntersectsSegment(bb, a, b);+}++void w_cpSpaceGetGravity(cpSpace *space, cpVect *out)+{+ *out = cpSpaceGetGravity(space);+}++void w_cpPinJointGetAnchorA(const cpConstraint *constraint, cpVect *out)+{+ *out = cpPinJointGetAnchorA(constraint);+}++void w_cpPinJointGetAnchorB(const cpConstraint *constraint, cpVect *out)+{+ *out = cpPinJointGetAnchorB(constraint);+}++void w_cpSlideJointGetAnchorA(const cpConstraint *constraint, cpVect *out)+{+ *out = cpSlideJointGetAnchorA(constraint);+}++void w_cpSlideJointGetAnchorB(const cpConstraint *constraint, cpVect *out)+{+ *out = cpSlideJointGetAnchorB(constraint);+}++void w_cpPivotJointGetAnchorA(const cpConstraint *constraint, cpVect *out)+{+ *out = cpPivotJointGetAnchorA(constraint);+}++void w_cpPivotJointGetAnchorB(const cpConstraint *constraint, cpVect *out)+{+ *out = cpPivotJointGetAnchorB(constraint);+}++void w_cpGrooveJointGetGrooveA(const cpConstraint *constraint, cpVect *out)+{+ *out = cpGrooveJointGetGrooveA(constraint);+}++void w_cpGrooveJointGetGrooveB(const cpConstraint *constraint, cpVect *out)+{+ *out = cpGrooveJointGetGrooveB(constraint);+}++void w_cpGrooveJointGetAnchorB(const cpConstraint *constraint, cpVect *out)+{+ *out = cpGrooveJointGetAnchorB(constraint);+}++void w_cpDampedSpringGetAnchorA(const cpConstraint *constraint, cpVect *out)+{+ *out = cpDampedSpringGetAnchorA(constraint);+}++void w_cpDampedSpringGetAnchorB(const cpConstraint *constraint, cpVect *out)+{+ *out = cpDampedSpringGetAnchorB(constraint);+}++void w_cpArbiterGetSurfaceVelocity(cpArbiter *arbiter, cpVect *out)+{+ *out = cpArbiterGetSurfaceVelocity(arbiter);+}++void w_cpArbiterGetNormal(const cpArbiter *arbiter, cpVect *out)+{+ *out = cpArbiterGetNormal(arbiter);+}++void w_cpArbiterGetPointA(const cpArbiter *arbiter, int i, cpVect *out)+{+ *out = cpArbiterGetPointA(arbiter, i);+}++void w_cpArbiterGetPointB(const cpArbiter *arbiter, int i, cpVect *out)+{+ *out = cpArbiterGetPointB(arbiter, i);+}