Files
cell/source/qjs_chipmunk.c
John Alanbrook ad4f3d3f58
Some checks failed
Build and Deploy / build-linux (push) Failing after 39s
Build and Deploy / build-windows (CLANG64) (push) Failing after 8m19s
Build and Deploy / package-dist (push) Has been skipped
Build and Deploy / deploy-itch (push) Has been skipped
Build and Deploy / deploy-gitea (push) Has been skipped
correct deletion
2025-02-26 10:07:32 -06:00

1279 lines
48 KiB
C

// qjs_chipmunk.c
#include <chipmunk/chipmunk.h>
#include <chipmunk/chipmunk_unsafe.h>
#include <quickjs.h>
#include <assert.h>
/*
Items are automatically reclaimed when they go out of scope in your quickjs program.
Each chipmunk type's userdata is a pointer to the underlying JSValue it's associated with.
Constraints and shapes are create and forget. They can't be changed between bodies. Just delete and make anew.
Memory for the underlying C types are only freed when the object is out of scope in javascript.
If a body is freed, all of its shapes and constraints are invalid and issue a warning if you try to use them.
*/
#define countof(x) (sizeof(x)/sizeof((x)[0]))
// Forward declarations of the prototypes we'll store globally
static JSValue circle_proto;
static JSValue segment_proto;
static JSValue poly_proto;
// For constraints
static JSValue js_gear;
static JSValue js_pin;
static JSValue js_pivot;
static JSValue js_rotary;
static JSValue js_motor;
static JSValue js_damped_spring;
static JSValue js_damped_rotary;
static JSValue js_groove;
static JSValue js_slide;
static JSValue js_ratchet;
static JSClassID js_cpSpace_class_id;
static JSClassID js_cpBody_class_id;
static JSClassID js_cpShape_class_id;
static JSClassID js_cpConstraint_class_id;
static void BodyFreeWrap(cpSpace *space, cpBody *body, void *unused) {
cpSpaceRemoveBody(space, body);
}
static void PostBodyFree(cpBody *body, cpSpace *space) {
cpSpaceAddPostStepCallback(space, BodyFreeWrap, body, NULL);
}
static void ShapeFreeWrap(cpSpace *space, cpShape *shape, void *unused) {
printf("Removing shape %p from space\n", shape);
cpSpace *myspace = cpShapeGetSpace(shape);
printf("i'm currently in %p, removing out of %p\n", myspace, space);
cpSpaceRemoveShape(space, shape);
}
static void PostShapeFree(cpShape *shape, cpSpace *space) {
cpSpaceAddPostStepCallback(space, ShapeFreeWrap, shape, NULL);
}
static void ConstraintFreeWrap(cpSpace *space, cpConstraint *constraint, void *unused) {
cpSpaceRemoveConstraint(space, constraint);
}
static void PostConstraintFree(cpConstraint *constraint, cpSpace *space) {
cpSpaceAddPostStepCallback(space, ConstraintFreeWrap, constraint, NULL);
}
static void js_cpSpace_finalizer(JSRuntime *rt, JSValue val)
{
printf("freeing space\n");
cpSpace *space = JS_GetOpaque(val, js_cpSpace_class_id);
cpSpaceEachShape(space, PostShapeFree, space);
cpSpaceEachConstraint(space, PostConstraintFree, space);
cpSpaceEachBody(space, PostBodyFree, space);
JSValue *cb = (JSValue*)cpShapeGetUserData(space);
JS_FreeValueRT(rt, *cb);
free(cb);
cpShapeSetUserData(space, NULL); // required to not crash on space free
cpSpaceFree(space);
}
static void body_rm_shape(cpBody *body, cpShape *shape, cpSpace *space)
{
cpSpaceRemoveShape(space, shape);
}
static void body_rm_constraint(cpBody *body, cpConstraint *constraint, cpSpace *space)
{
cpSpaceRemoveConstraint(space, constraint);
}
static void js_cpBody_finalizer(JSRuntime *rt, JSValue val)
{
printf("freeing body\n");
cpBody *body = JS_GetOpaque(val, js_cpBody_class_id);
cpSpace *space = cpBodyGetSpace(body);
if (space) {
cpBodyEachShape(body, body_rm_shape, space);
cpBodyEachConstraint(body, PostConstraintFree, space);
cpSpaceRemoveBody(space,body);
}
JSValue *cb = (JSValue *)cpBodyGetUserData(body);
JS_FreeValueRT(rt, *cb);
free(cb);
cpBodyFree(body);
}
static void js_cpShape_finalizer(JSRuntime *rt, JSValue val)
{
printf("freeing shape\n");
cpShape *shape = JS_GetOpaque(val, js_cpShape_class_id);
cpSpace *space = cpShapeGetSpace(shape);
if (space)
cpSpaceRemoveShape(space, shape);
JSValue *cb = (JSValue*)cpShapeGetUserData(shape);
JS_FreeValueRT(rt, *cb);
free(cb);
cpShapeFree(shape);
}
static void js_cpConstraint_finalizer(JSRuntime *rt, JSValue val)
{
printf("freeing constraint\n");
cpConstraint *joint = JS_GetOpaque(val, js_cpConstraint_class_id);
cpSpace *space = cpConstraintGetSpace(joint);
if (space)
cpSpaceRemoveConstraint(space,joint);
JSValue *cb = (JSValue*)cpConstraintGetUserData(joint);
JS_FreeValueRT(rt, *cb);
free(cb);
cpConstraintFree(joint);
}
#define CPCLASS(TYPE) \
static inline TYPE *js2##TYPE(JSContext *js, JSValue v) { \
return JS_GetOpaque(v, js_##TYPE##_class_id); \
} \
static inline JSValue TYPE##2js(JSContext *js, TYPE *cp) { \
return JS_DupValue(js, *(JSValue*)TYPE##GetUserData(cp)); \
} \
static inline void js_##TYPE##_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { \
TYPE *cp = JS_GetOpaque(val, js_##TYPE##_class_id); \
JS_MarkValue(rt, *(JSValue*)TYPE##GetUserData(cp), mark_func); \
} \
static JSClassDef js_##TYPE##_class = { \
#TYPE, \
.finalizer = js_##TYPE##_finalizer, \
.gc_mark = js_##TYPE##_mark, \
}; \
CPCLASS(cpShape)
CPCLASS(cpSpace)
CPCLASS(cpBody)
CPCLASS(cpConstraint)
static inline cpVect js2cpvect(JSContext *js, JSValue v) {
cpVect ret;
JSValue x = JS_GetPropertyStr(js, v, "x");
JSValue y = JS_GetPropertyStr(js, v, "y");
#if CP_USE_DOUBLES
JS_ToFloat64(js, &ret.x, x);
JS_ToFloat64(js, &ret.y, y);
#else
double fx, fy;
JS_ToFloat64(js, &fx, x);
JS_ToFloat64(js, &fy, y);
ret.x = (cpFloat)fx;
ret.y = (cpFloat)fy;
#endif
JS_FreeValue(js, x);
JS_FreeValue(js, y);
return ret;
}
static inline JSValue cpvect2js(JSContext *js, cpVect v) {
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "x", JS_NewFloat64(js, v.x));
JS_SetPropertyStr(js, obj, "y", JS_NewFloat64(js, v.y));
return obj;
}
static inline JSValue angle2js(JSContext *js, double angle) {
return JS_NewFloat64(js, angle);
}
static inline double js2angle(JSContext *js, JSValue v) {
double angle;
JS_ToFloat64(js, &angle, v);
return angle;
}
static inline JSValue number2js(JSContext *js, double number) {
return JS_NewFloat64(js, number);
}
static inline double js2number(JSContext *js, JSValue v) {
double num;
JS_ToFloat64(js, &num, v);
return num;
}
// CP Space GETSET macros
#define CP_GETSET(CP, ENTRY, TYPE) \
static JSValue js_##CP##_set_##ENTRY (JSContext *js, JSValueConst this_val, JSValue val) { \
CP *cp = js2##CP(js, this_val); \
if (!cp) return JS_EXCEPTION; \
CP##Set##ENTRY(cp, js2##TYPE(js, val)); \
return JS_UNDEFINED; \
} \
static JSValue js_##CP##_get_##ENTRY (JSContext *js, JSValueConst this_val) { \
CP *cp = js2##CP(js, this_val); \
if (!cp) return JS_EXCEPTION; \
return TYPE##2js(js, CP##Get##ENTRY(cp)); \
}
static JSValue js_make_cpSpace(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpSpace *space = cpSpaceNew();
JSValue obj = JS_NewObjectClass(js, js_cpSpace_class_id);
JS_SetOpaque(obj, space);
JSValue *cb = malloc(sizeof(*cb));
*cb = obj;//JS_DupValue(js,obj);
cpSpaceSetUserData(space, cb);
printf("new space is %p\n", space);
return obj;
}
static const JSCFunctionListEntry js_chipmunk2d_funcs[] = {
JS_CFUNC_DEF("make_space", 0, js_make_cpSpace),
};
// CP Space properties
CP_GETSET(cpSpace, Gravity, cpvect)
CP_GETSET(cpSpace, Iterations, number)
CP_GETSET(cpSpace, IdleSpeedThreshold, number)
CP_GETSET(cpSpace, SleepTimeThreshold, number)
CP_GETSET(cpSpace, CollisionSlop, number)
CP_GETSET(cpSpace, CollisionBias, number)
CP_GETSET(cpSpace, CollisionPersistence, number)
// Helpers for eachBody/eachShape/eachConstraint
struct contextfn {
JSContext *js;
JSValue fn;
};
typedef struct contextfn ctxfn;
static void each_body_callback(cpBody *body, void *data) {
ctxfn *pass = (ctxfn*)data;
JSValue arg = cpBody2js(pass->js, body);
JS_Call(pass->js, pass->fn, JS_UNDEFINED, 1, &arg);
JS_FreeValue(pass->js, arg);
}
static JSValue js_space_each_body(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpSpace *space = js2cpSpace(js, this_val);
if (!space) return JS_EXCEPTION;
if (!JS_IsFunction(js, argv[0])) {
return JS_ThrowTypeError(js, "Expected a function");
}
ctxfn pass;
pass.js = js;
pass.fn = JS_DupValue(js, argv[0]);
cpSpaceEachBody(space, each_body_callback, &pass);
JS_FreeValue(js, pass.fn);
return JS_UNDEFINED;
}
static void each_shape_callback(cpShape *shape, void *data) {
ctxfn *pass = (ctxfn *)data;
JSValue arg = cpShape2js(pass->js, shape);
JS_Call(pass->js, pass->fn, JS_UNDEFINED, 1, &arg);
JS_FreeValue(pass->js, arg);
}
static JSValue js_space_each_shape(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpSpace *space = js2cpSpace(js, this_val);
if (!space) return JS_EXCEPTION;
if (!JS_IsFunction(js, argv[0])) {
return JS_ThrowTypeError(js, "Expected a function");
}
ctxfn pass;
pass.js = js;
pass.fn = JS_DupValue(js, argv[0]);
cpSpaceEachShape(space, each_shape_callback, &pass);
JS_FreeValue(js, pass.fn);
return JS_UNDEFINED;
}
static void each_constraint_callback(cpConstraint *constraint, void *data) {
ctxfn *pass = (ctxfn*)data;
JSValue arg = cpConstraint2js(pass->js, constraint);
JS_Call(pass->js, pass->fn, JS_UNDEFINED, 1, &arg);
JS_FreeValue(pass->js, arg);
}
static JSValue js_space_each_constraint(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpSpace *space = js2cpSpace(js, this_val);
if (!space) return JS_EXCEPTION;
if (!JS_IsFunction(js, argv[0])) {
return JS_ThrowTypeError(js, "Expected a function");
}
ctxfn pass;
pass.js = js;
pass.fn = JS_DupValue(js, argv[0]);
cpSpaceEachConstraint(space, each_constraint_callback, &pass);
JS_FreeValue(js, pass.fn);
return JS_UNDEFINED;
}
static JSValue js_space_step (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
cpSpace *space = js2cpSpace(js, this_val);
if (!space) return JS_EXCEPTION;
cpSpaceStep(space, js2number(js, argv[0]));
return JS_UNDEFINED;
}
static JSValue js_cpSpace_add_body (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
cpSpace *space = js2cpSpace(js, this_val);
if (!space) return JS_ThrowReferenceError(js, "Not a space.");
cpBody *body = cpBodyNew(1,1);
cpBodySetType(body, CP_BODY_TYPE_DYNAMIC);
JSValue obj = JS_NewObjectClass(js, js_cpBody_class_id);
JS_SetOpaque(obj, body);
JSValue *cb = malloc(sizeof(*cb));
*cb = obj;//JS_DupValue(js,obj);
cpBodySetUserData(body, cb);
printf("adding body to space %p\n", space);
cpSpaceAddBody(space, body);
printf("finished adding body at %p\n", body);
return obj;
}
// CP Body GET/SET
#define JS_GETSET_BODY(NAME, TYPE) \
static JSValue js_body_set_##NAME (JSContext *js, JSValueConst this_val, JSValue val) { \
cpBody *body = js2cpBody(js, this_val); if (!body) return JS_EXCEPTION; \
cpBodySet##NAME(body, js2##TYPE(js, val)); \
return JS_UNDEFINED; \
} \
static JSValue js_body_get_##NAME (JSContext *js, JSValueConst this_val) { \
cpBody *body = js2cpBody(js, this_val); if (!body) return JS_EXCEPTION; \
return TYPE##2js(js, cpBodyGet##NAME(body)); \
}
// Body properties
JS_GETSET_BODY(Position, cpvect)
JS_GETSET_BODY(Angle, angle)
JS_GETSET_BODY(Velocity, cpvect)
JS_GETSET_BODY(AngularVelocity, angle)
JS_GETSET_BODY(Moment, number)
JS_GETSET_BODY(Torque, number)
JS_GETSET_BODY(Mass, number)
JS_GETSET_BODY(CenterOfGravity, cpvect)
JS_GETSET_BODY(Force, cpvect)
JS_GETSET_BODY(Type, number)
// Body methods
static JSValue js_body_apply_force_at_world_point(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpBody *body = js2cpBody(js, this_val); if (!body) return JS_EXCEPTION;
cpVect force = js2cpvect(js, argv[0]);
cpVect point = js2cpvect(js, argv[1]);
cpBodyApplyForceAtWorldPoint(body, force, point);
return JS_UNDEFINED;
}
static JSValue js_body_apply_force_at_local_point(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpBody *body = js2cpBody(js, this_val); if (!body) return JS_EXCEPTION;
cpVect force = js2cpvect(js, argv[0]);
cpVect point = js2cpvect(js, argv[1]);
cpBodyApplyForceAtLocalPoint(body, force, point);
return JS_UNDEFINED;
}
static JSValue js_body_apply_impulse_at_world_point(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpBody *body = js2cpBody(js, this_val); if (!body) return JS_EXCEPTION;
cpVect impulse = js2cpvect(js, argv[0]);
cpVect point = js2cpvect(js, argv[1]);
cpBodyApplyImpulseAtWorldPoint(body, impulse, point);
return JS_UNDEFINED;
}
static JSValue js_body_apply_impulse_at_local_point(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpBody *body = js2cpBody(js, this_val); if (!body) return JS_EXCEPTION;
cpVect impulse = js2cpvect(js, argv[0]);
cpVect point = js2cpvect(js, argv[1]);
cpBodyApplyImpulseAtLocalPoint(body, impulse, point);
return JS_UNDEFINED;
}
static JSValue js_body_is_sleeping(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpBody *body = js2cpBody(js, this_val); if (!body) return JS_EXCEPTION;
return JS_NewBool(js, cpBodyIsSleeping(body));
}
static JSValue js_body_activate(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpBody *body = js2cpBody(js, this_val); if (!body) return JS_EXCEPTION;
cpBodyActivate(body);
return JS_UNDEFINED;
}
static JSValue js_body_sleep(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpBody *body = js2cpBody(js, this_val); if (!body) return JS_EXCEPTION;
cpBodySleep(body);
return JS_UNDEFINED;
}
static JSValue js_body_activate_static(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpBody *body = js2cpBody(js, this_val); if (!body) return JS_EXCEPTION;
cpShape *filter = JS_IsUndefined(argv[0]) ? NULL : JS_GetOpaque(argv[0], js_cpShape_class_id);
cpBodyActivateStatic(body, filter);
return JS_UNDEFINED;
}
static JSValue js_body_sleep_with_group(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpBody *body = js2cpBody(js, this_val); if (!body) return JS_EXCEPTION;
cpBody *group = js2cpBody(js, argv[0]); if (!group) return JS_EXCEPTION;
cpBodySleepWithGroup(body, group);
return JS_UNDEFINED;
}
// Iteration over shapes, constraints, arbiters
static void body_shape_fn(cpBody *body, cpShape *shape, void *data) {
ctxfn *pass = data;
JSValue v = cpShape2js(pass->js, shape);
JS_Call(pass->js, pass->fn, JS_UNDEFINED, 1, &v);
JS_FreeValue(pass->js, v);
}
static JSValue js_body_each_shape(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpBody *body = js2cpBody(js, this_val); if (!body) return JS_EXCEPTION;
if (!JS_IsFunction(js, argv[0])) return JS_ThrowTypeError(js, "Expected a function");
ctxfn pass;
pass.js = js;
pass.fn = JS_DupValue(js, argv[0]);
cpBodyEachShape(body, body_shape_fn, &pass);
JS_FreeValue(js, pass.fn);
return JS_UNDEFINED;
}
static void body_constraint_fn(cpBody *body, cpConstraint *constraint, void *data) {
ctxfn *pass = data;
JSValue v = cpConstraint2js(pass->js, constraint);
JS_Call(pass->js, pass->fn, JS_UNDEFINED, 1, &v);
JS_FreeValue(pass->js, v);
}
static JSValue js_body_each_constraint(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpBody *body = js2cpBody(js, this_val); if (!body) return JS_EXCEPTION;
if (!JS_IsFunction(js, argv[0])) return JS_ThrowTypeError(js, "Expected a function");
ctxfn pass;
pass.js = js;
pass.fn = JS_DupValue(js, argv[0]);
cpBodyEachConstraint(body, body_constraint_fn, &pass);
JS_FreeValue(js, pass.fn);
return JS_UNDEFINED;
}
static void body_arbiter_fn(cpBody *body, cpArbiter *arbiter, ctxfn *pass) {
// Not fully implemented
}
static JSValue js_body_each_arbiter(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
// Not fully implemented
return JS_UNDEFINED;
}
static JSValue js_body_add_circle_shape(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpBody *body = js2cpBody(js, this_val);
if (!body) return JS_ThrowReferenceError(js, "Shape must be added to a body.");
cpSpace *space = cpBodyGetSpace(body);
printf("adding circle shape to space %p\n", space);
double radius = 10;
cpVect offset = {0,0};
if (argc > 0 && JS_IsObject(argv[0])) {
JSValue r = JS_GetPropertyStr(js, argv[0], "radius");
if (!JS_IsUndefined(r)) radius = js2number(js, r);
JS_FreeValue(js, r);
printf("RADIUS IS %g\n", radius);
JSValue o = JS_GetPropertyStr(js, argv[0], "offset");
if (!JS_IsUndefined(o)) offset = js2cpvect(js, o);
JS_FreeValue(js, o);
}
cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, offset));
JSValue obj = JS_NewObjectClass(js, js_cpShape_class_id);
JS_SetOpaque(obj, shape);
JS_SetPrototype(js, obj, circle_proto);
JSValue *cb = malloc(sizeof(*cb));
*cb = obj;//JS_DupValue(js,obj);
cpShapeSetUserData(shape, cb);
printf("MADE CIRCLE SHAPE\n");
return obj;
}
static JSValue js_body_add_segment_shape(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpBody *body = js2cpBody(js, this_val); if (!body) return JS_EXCEPTION;
cpVect a = js2cpvect(js, argv[0]);
cpVect b = js2cpvect(js, argv[1]);
double radius = js2number(js, argv[2]);
cpShape *shape = cpSegmentShapeNew(body, a, b, (cpFloat)radius);
JSValue obj = JS_NewObjectClass(js, js_cpShape_class_id);
JS_SetOpaque(obj, shape);
JS_SetPrototype(js, obj, segment_proto);
JSValue *cb = malloc(sizeof(*cb));
*cb = obj;//JS_DupValue(js,obj);
cpShapeSetUserData(shape, cb);
return obj;
}
static JSValue js_body_add_poly_shape(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpBody *body = js2cpBody(js, this_val); if (!body) return JS_EXCEPTION;
int count = (int)js2number(js, argv[0]);
// The actual verts array is not fully handled here. Just a placeholder for demonstration.
cpVect verts[1] = {{0,0}};
cpTransform T = cpTransformIdentity;
cpShape *shape = cpPolyShapeNew(body, count, verts, T, 0.0);
JSValue obj = JS_NewObjectClass(js, js_cpShape_class_id);
JS_SetOpaque(obj, shape);
JS_SetPrototype(js, obj, poly_proto);
JSValue *cb = malloc(sizeof(*cb));
*cb = JS_DupValue(js,obj);
cpShapeSetUserData(shape, cb);
return obj;
}
static const JSCFunctionListEntry js_cpBody_funcs[] = {
JS_CGETSET_DEF("position", js_body_get_Position, js_body_set_Position),
JS_CGETSET_DEF("angle", js_body_get_Angle, js_body_set_Angle),
JS_CGETSET_DEF("velocity", js_body_get_Velocity, js_body_set_Velocity),
JS_CGETSET_DEF("angularVelocity", js_body_get_AngularVelocity, js_body_set_AngularVelocity),
JS_CGETSET_DEF("moment", js_body_get_Moment, js_body_set_Moment),
JS_CGETSET_DEF("torque", js_body_get_Torque, js_body_set_Torque),
JS_CGETSET_DEF("mass", js_body_get_Mass, js_body_set_Mass),
JS_CGETSET_DEF("centerOfGravity", js_body_get_CenterOfGravity, js_body_set_CenterOfGravity),
JS_CGETSET_DEF("force", js_body_get_Force, js_body_set_Force),
JS_CGETSET_DEF("type", js_body_get_Type, js_body_set_Type),
JS_CFUNC_DEF("isSleeping", 0, js_body_is_sleeping),
JS_CFUNC_DEF("activate", 0, js_body_activate),
JS_CFUNC_DEF("sleep", 0, js_body_sleep),
JS_CFUNC_DEF("activateStatic", 1, js_body_activate_static),
JS_CFUNC_DEF("sleepWithGroup", 1, js_body_sleep_with_group),
JS_CFUNC_DEF("applyForceAtWorldPoint", 2, js_body_apply_force_at_world_point),
JS_CFUNC_DEF("applyForceAtLocalPoint", 2, js_body_apply_force_at_local_point),
JS_CFUNC_DEF("applyImpulseAtWorldPoint", 2, js_body_apply_impulse_at_world_point),
JS_CFUNC_DEF("applyImpulseAtLocalPoint", 2, js_body_apply_impulse_at_local_point),
JS_CFUNC_DEF("eachShape", 1, js_body_each_shape),
JS_CFUNC_DEF("eachConstraint", 1, js_body_each_constraint),
JS_CFUNC_DEF("eachArbiter", 1, js_body_each_arbiter),
JS_CFUNC_DEF("circle", 1, js_body_add_circle_shape),
JS_CFUNC_DEF("segment", 1, js_body_add_segment_shape),
JS_CFUNC_DEF("poly", 1, js_body_add_poly_shape),
};
// Shape-specific get/set
#define SHAPE_GETSET(SHAPE, ENTRY, TYPE) \
static JSValue js_##SHAPE##_set_##ENTRY (JSContext *js, JSValueConst this_val, JSValue val) { \
cpShape *shape = js2cpShape(js, this_val); \
if (!shape) return JS_EXCEPTION; \
SHAPE##Set##ENTRY(shape, js2##TYPE(js, val)); \
return JS_UNDEFINED; \
} \
static JSValue js_##SHAPE##_get_##ENTRY (JSContext *js, JSValueConst this_val) { \
cpShape *shape = js2cpShape(js, this_val); \
if (!shape) return JS_EXCEPTION; \
return TYPE##2js(js, SHAPE##Get##ENTRY(shape)); \
}
// Circle
SHAPE_GETSET(cpCircleShape, Radius, number)
SHAPE_GETSET(cpCircleShape, Offset, cpvect)
static const JSCFunctionListEntry js_cpCircleShape_funcs[] = {
JS_CGETSET_DEF("radius", js_cpCircleShape_get_Radius, js_cpCircleShape_set_Radius),
JS_CGETSET_DEF("offset", js_cpCircleShape_get_Offset, js_cpCircleShape_set_Offset),
};
// Segment
SHAPE_GETSET(cpSegmentShape, Radius, number)
static JSValue js_cpSegmentShape_set_endpoints(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpShape *shape = js2cpShape(js, this_val);
if (!shape) return JS_EXCEPTION;
cpSegmentShapeSetEndpoints(shape, js2cpvect(js, argv[0]), js2cpvect(js, argv[1]));
return JS_UNDEFINED;
}
static const JSCFunctionListEntry js_cpSegmentShape_funcs[] = {
JS_CFUNC_DEF("setEndpoints", 2, js_cpSegmentShape_set_endpoints),
JS_CGETSET_DEF("radius", js_cpSegmentShape_get_Radius, js_cpSegmentShape_set_Radius),
};
// Poly
SHAPE_GETSET(cpPolyShape, Radius, number)
static JSValue js_cpPolyShape_set_verts(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpShape *shape = js2cpShape(js, this_val);
if (!shape) return JS_EXCEPTION;
int count = (int)js2number(js, argv[0]);
// Not fully implemented
cpVect verts[1] = {{0,0}};
cpPolyShapeSetVerts(shape, count, verts, cpTransformIdentity);
return JS_UNDEFINED;
}
static const JSCFunctionListEntry js_cpPolyShape_funcs[] = {
JS_CFUNC_DEF("setVerts", 2, js_cpPolyShape_set_verts),
JS_CGETSET_DEF("radius", js_cpPolyShape_get_Radius, js_cpPolyShape_set_Radius),
};
// Generic shape fields
static JSValue js_shape_set_filter(JSContext *js, JSValueConst this_val, JSValue val) {
cpShape *shape = js2cpShape(js, this_val); if (!shape) return JS_EXCEPTION;
cpShapeFilter filter;
JSValue categories = JS_GetPropertyStr(js, val, "categories");
JSValue mask = JS_GetPropertyStr(js, val, "mask");
JSValue group = JS_GetPropertyStr(js, val, "group");
filter.categories = (uint32_t)js2number(js, categories);
filter.mask = (uint32_t)js2number(js, mask);
filter.group = (uint32_t)js2number(js, group);
JS_FreeValue(js, categories);
JS_FreeValue(js, mask);
JS_FreeValue(js, group);
cpShapeSetFilter(shape, filter);
return JS_UNDEFINED;
}
static JSValue js_shape_set_sensor(JSContext *js, JSValueConst this_val, JSValue val) {
cpShape *shape = js2cpShape(js, this_val); if (!shape) return JS_EXCEPTION;
cpShapeSetSensor(shape, JS_ToBool(js, val));
return JS_UNDEFINED;
}
static JSValue js_shape_get_sensor(JSContext *js, JSValueConst this_val) {
cpShape *shape = js2cpShape(js, this_val); if (!shape) return JS_EXCEPTION;
return JS_NewBool(js, cpShapeGetSensor(shape));
}
static JSValue js_shape_set_elasticity(JSContext *js, JSValueConst this_val, JSValue val) {
cpShape *shape = js2cpShape(js, this_val); if (!shape) return JS_EXCEPTION;
cpShapeSetElasticity(shape, js2number(js, val));
return JS_UNDEFINED;
}
static JSValue js_shape_get_elasticity(JSContext *js, JSValueConst this_val) {
cpShape *shape = js2cpShape(js, this_val); if (!shape) return JS_EXCEPTION;
return number2js(js, cpShapeGetElasticity(shape));
}
static JSValue js_shape_set_friction(JSContext *js, JSValueConst this_val, JSValue val) {
cpShape *shape = js2cpShape(js, this_val); if (!shape) return JS_EXCEPTION;
cpShapeSetFriction(shape, js2number(js, val));
return JS_UNDEFINED;
}
static JSValue js_shape_get_friction(JSContext *js, JSValueConst this_val) {
cpShape *shape = js2cpShape(js, this_val); if (!shape) return JS_EXCEPTION;
return number2js(js, cpShapeGetFriction(shape));
}
static JSValue js_shape_set_surface_velocity(JSContext *js, JSValueConst this_val, JSValue val) {
cpShape *shape = js2cpShape(js, this_val); if (!shape) return JS_EXCEPTION;
cpVect velocity = js2cpvect(js, val);
cpShapeSetSurfaceVelocity(shape, velocity);
return JS_UNDEFINED;
}
static JSValue js_shape_get_surface_velocity(JSContext *js, JSValueConst this_val) {
cpShape *shape = js2cpShape(js, this_val); if (!shape) return JS_EXCEPTION;
return cpvect2js(js, cpShapeGetSurfaceVelocity(shape));
}
static JSValue js_shape_get_collision_type(JSContext *js, JSValueConst this_val) {
cpShape *shape = js2cpShape(js, this_val); if (!shape) return JS_EXCEPTION;
return JS_NewInt32(js, cpShapeGetCollisionType(shape));
}
static JSValue js_shape_set_collision_type(JSContext *js, JSValueConst this_val, JSValue val) {
cpShape *shape = js2cpShape(js, this_val); if (!shape) return JS_EXCEPTION;
int32_t collision_type;
if (JS_ToInt32(js, &collision_type, val)) {
return JS_EXCEPTION;
}
cpShapeSetCollisionType(shape, collision_type);
return JS_UNDEFINED;
}
static JSValue js_shape_get_bb(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
cpShape *shape = js2cpShape(js, this_val); if (!shape) return JS_EXCEPTION;
cpBB bb = cpShapeGetBB(shape);
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "x", JS_NewFloat64(js, bb.l));
JS_SetPropertyStr(js, obj, "y", JS_NewFloat64(js, bb.b));
JS_SetPropertyStr(js, obj, "width", JS_NewFloat64(js, bb.r - bb.l));
JS_SetPropertyStr(js, obj, "height", JS_NewFloat64(js, bb.t - bb.b));
return obj;
}
static JSValue js_shape_get_filter(JSContext *js, JSValueConst this_val) {
cpShape *shape = js2cpShape(js, this_val); if (!shape) return JS_EXCEPTION;
cpShapeFilter filter = cpShapeGetFilter(shape);
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "categories", JS_NewUint32(js, filter.categories));
JS_SetPropertyStr(js, obj, "mask", JS_NewUint32(js, filter.mask));
JS_SetPropertyStr(js, obj, "group", JS_NewUint32(js, filter.group));
return obj;
}
static const JSCFunctionListEntry js_cpShape_funcs[] = {
JS_CGETSET_DEF("collisionType", js_shape_get_collision_type, js_shape_set_collision_type),
JS_CFUNC_DEF("getBB", 0, js_shape_get_bb),
JS_CGETSET_DEF("sensor", js_shape_get_sensor, js_shape_set_sensor),
JS_CGETSET_DEF("elasticity", js_shape_get_elasticity, js_shape_set_elasticity),
JS_CGETSET_DEF("friction", js_shape_get_friction, js_shape_set_friction),
JS_CGETSET_DEF("surfaceVelocity", js_shape_get_surface_velocity, js_shape_set_surface_velocity),
JS_CGETSET_DEF("filter", js_shape_get_filter, js_shape_set_filter)
};
// Constraints
#define CC_GETSET(CPTYPE, ENTRY, TYPE) \
static JSValue js_##CPTYPE##_get_##ENTRY (JSContext *js, JSValueConst this_val) { \
return TYPE##2js(js, CPTYPE##Get##ENTRY(js2cpConstraint(js, this_val))); \
} \
static JSValue js_##CPTYPE##_set_##ENTRY (JSContext *js, JSValueConst this_val, JSValue val) { \
CPTYPE##Set##ENTRY(js2cpConstraint(js, this_val), js2##TYPE(js, val)); \
return JS_UNDEFINED; \
}
CC_GETSET(cpConstraint, MaxForce, number)
CC_GETSET(cpConstraint, MaxBias, number)
CC_GETSET(cpConstraint, ErrorBias, number)
CC_GETSET(cpConstraint, CollideBodies, number)
static JSValue js_cpConstraint_broken (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
// We'll just return whether the constraint is still in the space or not
cpSpace *space = js2cpSpace(js, this_val);
if(!space) return JS_EXCEPTION;
cpConstraint *constraint = js2cpConstraint(js, argv[0]);
if(!constraint) return JS_FALSE;
cpBool inSpace = cpSpaceContainsConstraint(space, constraint);
return JS_NewBool(js, inSpace);
}
static JSValue js_cpConstraint_break (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
cpSpace *space = js2cpSpace(js, this_val);
if(!space) return JS_EXCEPTION;
cpConstraint *constraint = js2cpConstraint(js, argv[0]);
if(!constraint) return JS_UNDEFINED;
if(cpSpaceContainsConstraint(space, constraint)) {
cpSpaceRemoveConstraint(space, constraint);
}
return JS_UNDEFINED;
}
static JSValue js_cpConstraint_bodyA (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
cpBody *b = cpConstraintGetBodyA(js2cpConstraint(js, this_val));
return cpBody2js(js, b);
}
static JSValue js_cpConstraint_bodyB (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
cpBody *b = cpConstraintGetBodyB(js2cpConstraint(js, this_val));
return cpBody2js(js, b);
}
static const JSCFunctionListEntry js_cpConstraint_funcs[] = {
JS_CFUNC_DEF("bodyA", 0, js_cpConstraint_bodyA),
JS_CFUNC_DEF("bodyB", 0, js_cpConstraint_bodyB),
JS_CGETSET_DEF("max_force", js_cpConstraint_get_MaxForce, js_cpConstraint_set_MaxForce),
JS_CGETSET_DEF("max_bias", js_cpConstraint_get_MaxBias, js_cpConstraint_set_MaxBias),
JS_CGETSET_DEF("error_bias", js_cpConstraint_get_ErrorBias, js_cpConstraint_set_ErrorBias),
JS_CGETSET_DEF("collide_bodies", js_cpConstraint_get_CollideBodies, js_cpConstraint_set_CollideBodies),
JS_CFUNC_DEF("broken", 0, js_cpConstraint_broken),
JS_CFUNC_DEF("break", 0, js_cpConstraint_break),
};
static JSValue prep_constraint(JSContext *js, cpConstraint *c)
{
// Create a brand-new JS object of the cpConstraint class
JSValue ret = JS_NewObjectClass(js, js_cpConstraint_class_id);
// Attach the actual cpConstraint pointer
JS_SetOpaque(ret, c);
// Allocate space for the back-reference
JSValue *cb = malloc(sizeof(JSValue));
*cb = JS_DupValue(js,ret);
cpConstraintSetUserData(c, cb);
return ret;
}
// Pin joint
CC_GETSET(cpPinJoint, Dist, number)
CC_GETSET(cpPinJoint, AnchorA, cpvect)
CC_GETSET(cpPinJoint, AnchorB, cpvect)
static const JSCFunctionListEntry js_pin_funcs[] = {
JS_CGETSET_DEF("distance", js_cpPinJoint_get_Dist, js_cpPinJoint_set_Dist),
JS_CGETSET_DEF("anchor_a", js_cpPinJoint_get_AnchorA, js_cpPinJoint_set_AnchorA),
JS_CGETSET_DEF("anchor_b", js_cpPinJoint_get_AnchorB, js_cpPinJoint_set_AnchorB)
};
static JSValue js_joint_pin (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
// If called with no arguments, return the prototype object
if (JS_IsUndefined(argv[0])) {
return JS_DupValue(js, js_pin);
}
cpBody *bA = js2cpBody(js, argv[0]);
cpBody *bB = js2cpBody(js, argv[1]);
cpConstraint *c = cpPinJointNew(bA, bB, cpvzero, cpvzero);
cpSpaceAddConstraint(js2cpSpace(js, this_val), c);
JSValue pin = prep_constraint(js, c);
JS_SetPrototype(js, pin, js_pin);
return pin;
}
// Pivot joint
CC_GETSET(cpPivotJoint, AnchorA, cpvect)
CC_GETSET(cpPivotJoint, AnchorB, cpvect)
static const JSCFunctionListEntry js_pivot_funcs[] = {
JS_CGETSET_DEF("anchor_a", js_cpPivotJoint_get_AnchorA, js_cpPivotJoint_set_AnchorA),
JS_CGETSET_DEF("anchor_b", js_cpPivotJoint_get_AnchorB, js_cpPivotJoint_set_AnchorB)
};
static JSValue js_joint_pivot (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
if (JS_IsUndefined(argv[0])) {
return JS_DupValue(js, js_pivot);
}
cpBody *bA = js2cpBody(js, argv[0]);
cpBody *bB = js2cpBody(js, argv[1]);
cpVect pivotPoint = js2cpvect(js, argv[2]);
cpConstraint *c = cpPivotJointNew(bA, bB, pivotPoint);
cpSpaceAddConstraint(js2cpSpace(js, this_val), c);
JSValue pivot = prep_constraint(js, c);
JS_SetPrototype(js, pivot, js_pivot);
return pivot;
}
// Gear joint
CC_GETSET(cpGearJoint, Phase, number)
CC_GETSET(cpGearJoint, Ratio, number)
static const JSCFunctionListEntry js_gear_funcs[] = {
JS_CGETSET_DEF("phase", js_cpGearJoint_get_Phase, js_cpGearJoint_set_Phase),
JS_CGETSET_DEF("ratio", js_cpGearJoint_get_Ratio, js_cpGearJoint_set_Ratio),
};
static JSValue js_joint_gear (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
if (JS_IsUndefined(argv[0])) {
return JS_DupValue(js, js_gear);
}
cpBody *bA = js2cpBody(js, argv[0]);
cpBody *bB = js2cpBody(js, argv[1]);
double phase = js2number(js, argv[2]);
double ratio = js2number(js, argv[3]);
cpConstraint *c = cpGearJointNew(bA, bB, (cpFloat)phase, (cpFloat)ratio);
cpSpaceAddConstraint(js2cpSpace(js, this_val), c);
JSValue gear = prep_constraint(js, c);
JS_SetPrototype(js, gear, js_gear);
return gear;
}
// Rotary limit joint
CC_GETSET(cpRotaryLimitJoint, Min, number)
CC_GETSET(cpRotaryLimitJoint, Max, number)
static const JSCFunctionListEntry js_rotary_funcs[] = {
JS_CGETSET_DEF("min", js_cpRotaryLimitJoint_get_Min, js_cpRotaryLimitJoint_set_Min),
JS_CGETSET_DEF("max", js_cpRotaryLimitJoint_get_Max, js_cpRotaryLimitJoint_set_Max),
};
static JSValue js_joint_rotary (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
if (JS_IsUndefined(argv[0])) {
return JS_DupValue(js, js_rotary);
}
cpBody *bA = js2cpBody(js, argv[0]);
cpBody *bB = js2cpBody(js, argv[1]);
double min_ = js2number(js, argv[2]);
double max_ = js2number(js, argv[3]);
cpConstraint *c = cpRotaryLimitJointNew(bA, bB, (cpFloat)min_, (cpFloat)max_);
cpSpaceAddConstraint(js2cpSpace(js, this_val), c);
JSValue rotary = prep_constraint(js, c);
JS_SetPrototype(js, rotary, js_rotary);
return rotary;
}
// Damped Rotary Spring
CC_GETSET(cpDampedRotarySpring, RestAngle, number)
CC_GETSET(cpDampedRotarySpring, Stiffness, number)
CC_GETSET(cpDampedRotarySpring, Damping, number)
static const JSCFunctionListEntry js_damped_rotary_funcs[] = {
JS_CGETSET_DEF("rest_angle", js_cpDampedRotarySpring_get_RestAngle, js_cpDampedRotarySpring_set_RestAngle),
JS_CGETSET_DEF("stiffness", js_cpDampedRotarySpring_get_Stiffness, js_cpDampedRotarySpring_set_Stiffness),
JS_CGETSET_DEF("damping", js_cpDampedRotarySpring_get_Damping, js_cpDampedRotarySpring_set_Damping),
};
static JSValue js_joint_damped_rotary (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
if (JS_IsUndefined(argv[0])) {
return JS_DupValue(js, js_damped_rotary);
}
cpBody *bA = js2cpBody(js, argv[0]);
cpBody *bB = js2cpBody(js, argv[1]);
double rest = js2number(js, argv[2]);
double stiffness = js2number(js, argv[3]);
double damping = js2number(js, argv[4]);
cpConstraint *c = cpDampedRotarySpringNew(bA, bB, (cpFloat)rest, (cpFloat)stiffness, (cpFloat)damping);
cpSpaceAddConstraint(js2cpSpace(js, this_val), c);
JSValue dr = prep_constraint(js, c);
JS_SetPrototype(js, dr, js_damped_rotary);
return dr;
}
// Damped Spring
CC_GETSET(cpDampedSpring, AnchorA, cpvect)
CC_GETSET(cpDampedSpring, AnchorB, cpvect)
CC_GETSET(cpDampedSpring, RestLength, number)
CC_GETSET(cpDampedSpring, Stiffness, number)
CC_GETSET(cpDampedSpring, Damping, number)
static const JSCFunctionListEntry js_damped_spring_funcs[] = {
JS_CGETSET_DEF("anchor_a", js_cpDampedSpring_get_AnchorA, js_cpDampedSpring_set_AnchorA),
JS_CGETSET_DEF("anchor_b", js_cpDampedSpring_get_AnchorB, js_cpDampedSpring_set_AnchorB),
JS_CGETSET_DEF("rest_length",js_cpDampedSpring_get_RestLength,js_cpDampedSpring_set_RestLength),
JS_CGETSET_DEF("stiffness", js_cpDampedSpring_get_Stiffness, js_cpDampedSpring_set_Stiffness),
JS_CGETSET_DEF("damping", js_cpDampedSpring_get_Damping, js_cpDampedSpring_set_Damping),
};
static JSValue js_joint_damped_spring (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
if (JS_IsUndefined(argv[0])) {
return JS_DupValue(js, js_damped_spring);
}
cpBody *bA = js2cpBody(js, argv[0]);
cpBody *bB = js2cpBody(js, argv[1]);
cpVect aA = js2cpvect(js, argv[2]);
cpVect aB = js2cpvect(js, argv[3]);
double restLength = js2number(js, argv[4]);
double stiffness = js2number(js, argv[5]);
double damping = js2number(js, argv[6]);
cpConstraint *c = cpDampedSpringNew(bA, bB, aA, aB, (cpFloat)restLength, (cpFloat)stiffness, (cpFloat)damping);
cpSpaceAddConstraint(js2cpSpace(js, this_val), c);
JSValue ds = prep_constraint(js, c);
JS_SetPrototype(js, ds, js_damped_spring);
return ds;
}
// Groove joint
CC_GETSET(cpGrooveJoint, GrooveA, cpvect)
CC_GETSET(cpGrooveJoint, GrooveB, cpvect)
CC_GETSET(cpGrooveJoint, AnchorB, cpvect)
static const JSCFunctionListEntry js_groove_funcs[] = {
JS_CGETSET_DEF("groove_a", js_cpGrooveJoint_get_GrooveA, js_cpGrooveJoint_set_GrooveA),
JS_CGETSET_DEF("groove_b", js_cpGrooveJoint_get_GrooveB, js_cpGrooveJoint_set_GrooveB),
JS_CGETSET_DEF("anchor_b", js_cpGrooveJoint_get_AnchorB, js_cpGrooveJoint_set_AnchorB),
};
static JSValue js_joint_groove (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
if (JS_IsUndefined(argv[0])) {
return JS_DupValue(js, js_groove);
}
cpBody *bA = js2cpBody(js, argv[0]);
cpBody *bB = js2cpBody(js, argv[1]);
cpVect ga = js2cpvect(js, argv[2]);
cpVect gb = js2cpvect(js, argv[3]);
cpVect ab = js2cpvect(js, argv[4]);
cpConstraint *c = cpGrooveJointNew(bA, bB, ga, gb, ab);
cpSpaceAddConstraint(js2cpSpace(js, this_val), c);
JSValue gz = prep_constraint(js, c);
JS_SetPrototype(js, gz, js_groove);
return gz;
}
// Slide joint
CC_GETSET(cpSlideJoint, AnchorA, cpvect)
CC_GETSET(cpSlideJoint, AnchorB, cpvect)
CC_GETSET(cpSlideJoint, Min, number)
CC_GETSET(cpSlideJoint, Max, number)
static const JSCFunctionListEntry js_slide_funcs[] = {
JS_CGETSET_DEF("anchor_a", js_cpSlideJoint_get_AnchorA, js_cpSlideJoint_set_AnchorA),
JS_CGETSET_DEF("anchor_b", js_cpSlideJoint_get_AnchorB, js_cpSlideJoint_set_AnchorB),
JS_CGETSET_DEF("min", js_cpSlideJoint_get_Min, js_cpSlideJoint_set_Min),
JS_CGETSET_DEF("max", js_cpSlideJoint_get_Max, js_cpSlideJoint_set_Max),
};
static JSValue js_joint_slide (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
if (JS_IsUndefined(argv[0])) {
return JS_DupValue(js, js_slide);
}
cpBody *bA = js2cpBody(js, argv[0]);
cpBody *bB = js2cpBody(js, argv[1]);
cpVect aA = js2cpvect(js, argv[2]);
cpVect aB = js2cpvect(js, argv[3]);
double min_ = js2number(js, argv[4]);
double max_ = js2number(js, argv[5]);
cpConstraint *c = cpSlideJointNew(bA, bB, aA, aB, (cpFloat)min_, (cpFloat)max_);
cpSpaceAddConstraint(js2cpSpace(js, this_val), c);
JSValue sld = prep_constraint(js, c);
JS_SetPrototype(js, sld, js_slide);
return sld;
}
// Ratchet joint
CC_GETSET(cpRatchetJoint, Angle, number)
CC_GETSET(cpRatchetJoint, Phase, number)
CC_GETSET(cpRatchetJoint, Ratchet, number)
static const JSCFunctionListEntry js_ratchet_funcs[] = {
JS_CGETSET_DEF("angle", js_cpRatchetJoint_get_Angle, js_cpRatchetJoint_set_Angle),
JS_CGETSET_DEF("phase", js_cpRatchetJoint_get_Phase, js_cpRatchetJoint_set_Phase),
JS_CGETSET_DEF("ratchet", js_cpRatchetJoint_get_Ratchet, js_cpRatchetJoint_set_Ratchet),
};
static JSValue js_joint_ratchet (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
if (JS_IsUndefined(argv[0])) {
return JS_DupValue(js, js_ratchet);
}
cpBody *bA = js2cpBody(js, argv[0]);
cpBody *bB = js2cpBody(js, argv[1]);
double phase = js2number(js, argv[2]);
double rat = js2number(js, argv[3]);
cpConstraint *c = cpRatchetJointNew(bA, bB, (cpFloat)phase, (cpFloat)rat);
cpSpaceAddConstraint(js2cpSpace(js, this_val), c);
JSValue r = prep_constraint(js, c);
JS_SetPrototype(js, r, js_ratchet);
return r;
}
// Simple motor
CC_GETSET(cpSimpleMotor, Rate, number)
static const JSCFunctionListEntry js_motor_funcs[] = {
JS_CGETSET_DEF("rate", js_cpSimpleMotor_get_Rate, js_cpSimpleMotor_set_Rate),
};
static JSValue js_joint_motor (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) {
if (JS_IsUndefined(argv[0])) {
return JS_DupValue(js, js_motor);
}
cpBody *bA = js2cpBody(js, argv[0]);
cpBody *bB = js2cpBody(js, argv[1]);
double rate = js2number(js, argv[2]);
cpConstraint *c = cpSimpleMotorNew(bA, bB, (cpFloat)rate);
cpSpaceAddConstraint(js2cpSpace(js, this_val), c);
JSValue m = prep_constraint(js, c);
JS_SetPrototype(js, m, js_motor);
return m;
}
// Space methods
static const JSCFunctionListEntry js_cpSpace_funcs[] = {
JS_CFUNC_DEF("body", 0, js_cpSpace_add_body),
JS_CFUNC_DEF("step", 1, js_space_step),
JS_CGETSET_DEF("gravity", js_cpSpace_get_Gravity, js_cpSpace_set_Gravity),
JS_CGETSET_DEF("iterations", js_cpSpace_get_Iterations, js_cpSpace_set_Iterations),
JS_CFUNC_DEF("eachBody", 1, js_space_each_body),
JS_CFUNC_DEF("eachShape", 1, js_space_each_shape),
JS_CFUNC_DEF("eachConstraint", 1, js_space_each_constraint),
JS_CGETSET_DEF("idle_speed", js_cpSpace_get_IdleSpeedThreshold, js_cpSpace_set_IdleSpeedThreshold),
JS_CGETSET_DEF("sleep_time", js_cpSpace_get_SleepTimeThreshold, js_cpSpace_set_SleepTimeThreshold),
JS_CGETSET_DEF("collision_slop", js_cpSpace_get_CollisionSlop, js_cpSpace_set_CollisionSlop),
JS_CGETSET_DEF("collision_bias", js_cpSpace_get_CollisionBias, js_cpSpace_set_CollisionBias),
JS_CGETSET_DEF("collision_persistence", js_cpSpace_get_CollisionPersistence, js_cpSpace_set_CollisionPersistence),
// Joint creation
JS_CFUNC_DEF("pin", 2, js_joint_pin),
JS_CFUNC_DEF("pivot", 3, js_joint_pivot),
JS_CFUNC_DEF("gear", 4, js_joint_gear),
JS_CFUNC_DEF("rotary", 4, js_joint_rotary),
JS_CFUNC_DEF("damped_rotary", 5, js_joint_damped_rotary),
JS_CFUNC_DEF("damped_spring", 7, js_joint_damped_spring),
JS_CFUNC_DEF("groove", 5, js_joint_groove),
JS_CFUNC_DEF("slide", 6, js_joint_slide),
JS_CFUNC_DEF("ratchet", 4, js_joint_ratchet),
JS_CFUNC_DEF("motor", 3, js_joint_motor),
};
// qjs_chipmunk.c - updated
JSValue js_chipmunk2d_use(JSContext *js) {
// Register cpSpace class
JS_NewClassID(&js_cpSpace_class_id);
JS_NewClass(JS_GetRuntime(js), js_cpSpace_class_id, &js_cpSpace_class);
JSValue space_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, space_proto, js_cpSpace_funcs, countof(js_cpSpace_funcs));
JS_SetClassProto(js, js_cpSpace_class_id, space_proto);
// Register cpBody class
JS_NewClassID(&js_cpBody_class_id);
JS_NewClass(JS_GetRuntime(js), js_cpBody_class_id, &js_cpBody_class);
JSValue body_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, body_proto, js_cpBody_funcs, countof(js_cpBody_funcs));
JS_SetClassProto(js, js_cpBody_class_id, body_proto);
// Register cpShape class
JS_NewClassID(&js_cpShape_class_id);
JS_NewClass(JS_GetRuntime(js), js_cpShape_class_id, &js_cpShape_class);
JSValue shape_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, shape_proto, js_cpShape_funcs, countof(js_cpShape_funcs));
JS_SetClassProto(js, js_cpShape_class_id, shape_proto);
// Register cpConstraint class
JS_NewClassID(&js_cpConstraint_class_id);
JS_NewClass(JS_GetRuntime(js), js_cpConstraint_class_id, &js_cpConstraint_class);
JSValue constraint_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, constraint_proto, js_cpConstraint_funcs, countof(js_cpConstraint_funcs));
JS_SetClassProto(js, js_cpConstraint_class_id, constraint_proto);
// --- Shape Subtype Prototypes ---
JSValue circle_proto_local = JS_NewObject(js);
JS_SetPropertyFunctionList(js, circle_proto_local, js_cpCircleShape_funcs, countof(js_cpCircleShape_funcs));
JS_SetPrototype(js, circle_proto_local, shape_proto);
JSValue segment_proto_local = JS_NewObject(js);
JS_SetPropertyFunctionList(js, segment_proto_local, js_cpSegmentShape_funcs, countof(js_cpSegmentShape_funcs));
JS_SetPrototype(js, segment_proto_local, shape_proto);
JSValue poly_proto_local = JS_NewObject(js);
JS_SetPropertyFunctionList(js, poly_proto_local, js_cpPolyShape_funcs, countof(js_cpPolyShape_funcs));
JS_SetPrototype(js, poly_proto_local, shape_proto);
// Store shape subtypes globally
circle_proto = JS_DupValue(js, circle_proto_local);
segment_proto = JS_DupValue(js, segment_proto_local);
poly_proto = JS_DupValue(js, poly_proto_local);
// --- Constraint Subtype Prototypes ---
JSValue pin_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, pin_proto, js_pin_funcs, countof(js_pin_funcs));
JS_SetPrototype(js, pin_proto, constraint_proto);
JSValue pivot_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, pivot_proto, js_pivot_funcs, countof(js_pivot_funcs));
JS_SetPrototype(js, pivot_proto, constraint_proto);
JSValue gear_proto_local = JS_NewObject(js);
JS_SetPropertyFunctionList(js, gear_proto_local, js_gear_funcs, countof(js_gear_funcs));
JS_SetPrototype(js, gear_proto_local, constraint_proto);
JSValue rotary_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, rotary_proto, js_rotary_funcs, countof(js_rotary_funcs));
JS_SetPrototype(js, rotary_proto, constraint_proto);
JSValue motor_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, motor_proto, js_motor_funcs, countof(js_motor_funcs));
JS_SetPrototype(js, motor_proto, constraint_proto);
JSValue damped_rotary_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, damped_rotary_proto, js_damped_rotary_funcs, countof(js_damped_rotary_funcs));
JS_SetPrototype(js, damped_rotary_proto, constraint_proto);
JSValue damped_spring_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, damped_spring_proto, js_damped_spring_funcs, countof(js_damped_spring_funcs));
JS_SetPrototype(js, damped_spring_proto, constraint_proto);
JSValue groove_proto_local = JS_NewObject(js);
JS_SetPropertyFunctionList(js, groove_proto_local, js_groove_funcs, countof(js_groove_funcs));
JS_SetPrototype(js, groove_proto_local, constraint_proto);
JSValue slide_proto_local = JS_NewObject(js);
JS_SetPropertyFunctionList(js, slide_proto_local, js_slide_funcs, countof(js_slide_funcs));
JS_SetPrototype(js, slide_proto_local, constraint_proto);
JSValue ratchet_proto_local = JS_NewObject(js);
JS_SetPropertyFunctionList(js, ratchet_proto_local, js_ratchet_funcs, countof(js_ratchet_funcs));
JS_SetPrototype(js, ratchet_proto_local, constraint_proto);
// Store constraint subtypes globally
js_pin = JS_DupValue(js, pin_proto);
js_pivot = JS_DupValue(js, pivot_proto);
js_gear = JS_DupValue(js, gear_proto_local);
js_rotary = JS_DupValue(js, rotary_proto);
js_motor = JS_DupValue(js, motor_proto);
js_damped_rotary = JS_DupValue(js, damped_rotary_proto);
js_damped_spring = JS_DupValue(js, damped_spring_proto);
js_groove = JS_DupValue(js, groove_proto_local);
js_slide = JS_DupValue(js, slide_proto_local);
js_ratchet = JS_DupValue(js, ratchet_proto_local);
// Store references in prosperon.c_types
JSValue global = JS_GetGlobalObject(js);
JSValue prosp = JS_GetPropertyStr(js, global, "prosperon");
JSValue c_types = JS_GetPropertyStr(js, prosp, "c_types");
// Store base types
JS_SetPropertyStr(js, c_types, "cpSpace", JS_DupValue(js, space_proto));
JS_SetPropertyStr(js, c_types, "cpBody", JS_DupValue(js, body_proto));
JS_SetPropertyStr(js, c_types, "cpShape", JS_DupValue(js, shape_proto));
JS_SetPropertyStr(js, c_types, "cpConstraint", JS_DupValue(js, constraint_proto));
// Store shape subtypes
JS_SetPropertyStr(js, c_types, "cpCircleShape", JS_DupValue(js, circle_proto_local));
JS_SetPropertyStr(js, c_types, "cpSegmentShape", JS_DupValue(js, segment_proto_local));
JS_SetPropertyStr(js, c_types, "cpPolyShape", JS_DupValue(js, poly_proto_local));
// Store constraint subtypes
JS_SetPropertyStr(js, c_types, "cpPinJoint", JS_DupValue(js, pin_proto));
JS_SetPropertyStr(js, c_types, "cpPivotJoint", JS_DupValue(js, pivot_proto));
JS_SetPropertyStr(js, c_types, "cpGearJoint", JS_DupValue(js, gear_proto_local));
JS_SetPropertyStr(js, c_types, "cpRotaryLimitJoint", JS_DupValue(js, rotary_proto));
JS_SetPropertyStr(js, c_types, "cpSimpleMotor", JS_DupValue(js, motor_proto));
JS_SetPropertyStr(js, c_types, "cpDampedRotarySpring", JS_DupValue(js, damped_rotary_proto));
JS_SetPropertyStr(js, c_types, "cpDampedSpring", JS_DupValue(js, damped_spring_proto));
JS_SetPropertyStr(js, c_types, "cpGrooveJoint", JS_DupValue(js, groove_proto_local));
JS_SetPropertyStr(js, c_types, "cpSlideJoint", JS_DupValue(js, slide_proto_local));
JS_SetPropertyStr(js, c_types, "cpRatchetJoint", JS_DupValue(js, ratchet_proto_local));
// Clean up
JS_FreeValue(js, c_types);
JS_FreeValue(js, prosp);
JS_FreeValue(js, global);
// Create the export object with the top-level function(s)
JSValue export_obj = JS_NewObject(js);
JS_SetPropertyFunctionList(js, export_obj, js_chipmunk2d_funcs, countof(js_chipmunk2d_funcs));
return export_obj;
}
static int js_chipmunk2d_init(JSContext *js, JSModuleDef *m) {
return JS_SetModuleExport(js, m, "default", js_chipmunk2d_use(js));
}
#ifdef JS_SHARED_LIBRARY
#define JS_INIT_MODULE js_init_module
#else
#define JS_INIT_MODULE js_init_module_chipmunk
#endif
JSModuleDef *JS_INIT_MODULE(JSContext *js, const char *name) {
JSModuleDef *m = JS_NewCModule(js, name, js_chipmunk2d_init);
if (!m)
return NULL;
JS_AddModuleExport(js, m, "Space");
return m;
}