changeset 16:af2f61a96318

ported chipmunk demos
author Extrawurst
date Sat, 04 Dec 2010 02:02:29 +0100
parents df4ebc8add66
children 131331ebb599
files trunk/tests/ChipmunkDemos/drawSpace.d trunk/tests/ChipmunkDemos/framework.d trunk/tests/ChipmunkDemos/gameApp.d trunk/tests/ChipmunkDemos/main.d trunk/tests/ChipmunkDemos/samples/Bounce.d trunk/tests/ChipmunkDemos/samples/ChipmunkDemo.d trunk/tests/ChipmunkDemos/samples/Joints.d trunk/tests/ChipmunkDemos/samples/LogoSmash.d trunk/tests/ChipmunkDemos/samples/MagnetsElectric.d trunk/tests/ChipmunkDemos/samples/OneWay.d trunk/tests/ChipmunkDemos/samples/Planet.d trunk/tests/ChipmunkDemos/samples/Player.d trunk/tests/ChipmunkDemos/samples/Plink.d trunk/tests/ChipmunkDemos/samples/Pump.d trunk/tests/ChipmunkDemos/samples/PyramidStack.d trunk/tests/ChipmunkDemos/samples/PyramidTopple.d trunk/tests/ChipmunkDemos/samples/Query.d trunk/tests/ChipmunkDemos/samples/Sensors.d trunk/tests/ChipmunkDemos/samples/Simple.d trunk/tests/ChipmunkDemos/samples/Springies.d trunk/tests/ChipmunkDemos/samples/Tank.d trunk/tests/ChipmunkDemos/samples/TheoJansen.d trunk/tests/ChipmunkDemos/samples/Tumble.d trunk/tests/ChipmunkDemos/samples/UnsafeOps.d
diffstat 24 files changed, 3984 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/drawSpace.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,510 @@
+/* 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.
+ */
+module drawSpace;
+
+import derelict.opengl.gl;
+
+import chipmunkd.chipmunk;
+
+import std.math:PI;
+import std.stdio;
+
+struct drawSpaceOptions {
+	int drawHash;
+	int drawBBs;
+	int drawShapes;
+	float collisionPointSize;
+	float bodyPointSize;
+	float lineThickness;
+}
+
+/*
+	IMPORTANT - READ ME!
+	
+	This file sets up a simple interface that the individual demos can use to get
+	a Chipmunk space running and draw what's in it. In order to keep the Chipmunk
+	examples clean and simple, they contain no graphics code. All drawing is done
+	by accessing the Chipmunk structures at a very low level. It is NOT
+	recommended to write a game or application this way as it does not scale
+	beyond simple shape drawing and is very dependent on implementation details
+	about Chipmunk which may change with little to no warning.
+*/
+
+enum float[3] LINE_COLOR = [0,0,0];
+enum float[3] COLLISION_COLOR = [1,0,0];
+enum float[3] BODY_COLOR = [0,0,1];
+
+static void
+glColor_from_pointer(void *ptr)
+{
+	ulong val = cast(long)ptr;
+	
+	// hash the pointer up nicely
+	val = (val+0x7ed55d16) + (val<<12);
+	val = (val^0xc761c23c) ^ (val>>19);
+	val = (val+0x165667b1) + (val<<5);
+	val = (val+0xd3a2646c) ^ (val<<9);
+	val = (val+0xfd7046c5) + (val<<3);
+	val = (val^0xb55a4f09) ^ (val>>16);
+	
+//	GLfloat v = (GLfloat)val/(GLfloat)ULONG_MAX;
+//	v = 0.95f - v*0.15f;
+//	
+//	glColor3f(v, v, v);
+
+	GLubyte r = (val>>0) & 0xFF;
+	GLubyte g = (val>>8) & 0xFF;
+	GLubyte b = (val>>16) & 0xFF;
+	
+	GLubyte max = r>g ? (r>b ? r : b) : (g>b ? g : b);
+	
+	const int mult = 127;
+	const int add = 63;
+	r = cast(ubyte)((r*mult)/max + add);
+	g = cast(ubyte)((g*mult)/max + add);
+	b = cast(ubyte)((b*mult)/max + add);
+	
+	glColor3ub(r, g, b);
+}
+
+static void
+glColor_for_shape(cpShape *shape, cpSpace *space)
+{
+	cpBody *_body = shape._body;
+	if(_body){
+		if(_body.node.next){
+			GLfloat v = 0.25f;
+			glColor3f(v,v,v);
+			return;
+		} else if(_body.node.idleTime > space.sleepTimeThreshold) {
+			GLfloat v = 0.9f;
+			glColor3f(v,v,v);
+			return;
+		}
+	}
+	
+	glColor_from_pointer(shape);
+}
+
+enum GLfloat[] circleVAR = [
+	 0.0000f,  1.0000f,
+	 0.2588f,  0.9659f,
+	 0.5000f,  0.8660f,
+	 0.7071f,  0.7071f,
+	 0.8660f,  0.5000f,
+	 0.9659f,  0.2588f,
+	 1.0000f,  0.0000f,
+	 0.9659f, -0.2588f,
+	 0.8660f, -0.5000f,
+	 0.7071f, -0.7071f,
+	 0.5000f, -0.8660f,
+	 0.2588f, -0.9659f,
+	 0.0000f, -1.0000f,
+	-0.2588f, -0.9659f,
+	-0.5000f, -0.8660f,
+	-0.7071f, -0.7071f,
+	-0.8660f, -0.5000f,
+	-0.9659f, -0.2588f,
+	-1.0000f, -0.0000f,
+	-0.9659f,  0.2588f,
+	-0.8660f,  0.5000f,
+	-0.7071f,  0.7071f,
+	-0.5000f,  0.8660f,
+	-0.2588f,  0.9659f,
+	 0.0000f,  1.0000f,
+	 0.0f, 0.0f, // For an extra line to see the rotation.
+];
+enum int circleVAR_count = circleVAR.length / 2;
+
+static void
+drawCircleShape(cpBody *_body, cpCircleShape *circle, cpSpace *space)
+{
+	glVertexPointer(2, GL_FLOAT, 0, circleVAR.ptr);
+	
+	glPushMatrix(); {
+		cpVect center = circle.tc;
+		glTranslatef(center.x, center.y, 0.0f);
+		glRotatef(_body.a*180.0f/PI, 0.0f, 0.0f, 1.0f);
+		glScalef(circle.r, circle.r, 1.0f);
+		
+		if(!circle.shape.sensor){
+			glColor_for_shape(cast(cpShape *)circle, space);
+			glDrawArrays(GL_TRIANGLE_FAN, 0, circleVAR_count - 1);
+		}
+		
+		glColor3fv(LINE_COLOR.ptr);
+		glDrawArrays(GL_LINE_STRIP, 0, circleVAR_count);
+	} glPopMatrix();
+}
+
+enum GLfloat[] pillVAR = [
+	 0.0000f,  1.0000f, 1.0f,
+	 0.2588f,  0.9659f, 1.0f,
+	 0.5000f,  0.8660f, 1.0f,
+	 0.7071f,  0.7071f, 1.0f,
+	 0.8660f,  0.5000f, 1.0f,
+	 0.9659f,  0.2588f, 1.0f,
+	 1.0000f,  0.0000f, 1.0f,
+	 0.9659f, -0.2588f, 1.0f,
+	 0.8660f, -0.5000f, 1.0f,
+	 0.7071f, -0.7071f, 1.0f,
+	 0.5000f, -0.8660f, 1.0f,
+	 0.2588f, -0.9659f, 1.0f,
+	 0.0000f, -1.0000f, 1.0f,
+
+	 0.0000f, -1.0000f, 0.0f,
+	-0.2588f, -0.9659f, 0.0f,
+	-0.5000f, -0.8660f, 0.0f,
+	-0.7071f, -0.7071f, 0.0f,
+	-0.8660f, -0.5000f, 0.0f,
+	-0.9659f, -0.2588f, 0.0f,
+	-1.0000f, -0.0000f, 0.0f,
+	-0.9659f,  0.2588f, 0.0f,
+	-0.8660f,  0.5000f, 0.0f,
+	-0.7071f,  0.7071f, 0.0f,
+	-0.5000f,  0.8660f, 0.0f,
+	-0.2588f,  0.9659f, 0.0f,
+	 0.0000f,  1.0000f, 0.0f,
+];
+enum int pillVAR_count = pillVAR.length/3;
+
+static void
+drawSegmentShape(cpBody *_body, cpSegmentShape *seg, cpSpace *space)
+{
+	cpVect a = seg.ta;
+	cpVect b = seg.tb;
+	
+	if(seg.r){
+		glVertexPointer(3, GL_FLOAT, 0, pillVAR.ptr);
+		glPushMatrix(); {
+			cpVect d = cpvsub(b, a);
+			cpVect r = cpvmult(d, seg.r/cpvlength(d));
+
+			GLfloat matrix[] = [
+				 r.x, r.y, 0.0f, 0.0f,
+				-r.y, r.x, 0.0f, 0.0f,
+				 d.x, d.y, 0.0f, 0.0f,
+				 a.x, a.y, 0.0f, 1.0f,
+			];
+			glMultMatrixf(matrix.ptr);
+			
+			if(!seg.shape.sensor){
+				glColor_for_shape(cast(cpShape *)seg, space);
+				glDrawArrays(GL_TRIANGLE_FAN, 0, pillVAR_count);
+			}
+			
+			glColor3fv(LINE_COLOR.ptr);
+			glDrawArrays(GL_LINE_LOOP, 0, pillVAR_count);
+		} glPopMatrix();
+	} else {
+		glColor3fv(LINE_COLOR.ptr);
+		glBegin(GL_LINES); {
+			glVertex2f(a.x, a.y);
+			glVertex2f(b.x, b.y);
+		} glEnd();
+	}
+}
+
+static void
+drawPolyShape(cpBody *_body, cpPolyShape *poly, cpSpace *space)
+{
+	int count = poly.numVerts;
+version(CP_USE_DOUBLES)
+{
+	glVertexPointer(2, GL_DOUBLE, 0, poly.tVerts);
+}
+else
+{
+	glVertexPointer(2, GL_FLOAT, 0, poly.tVerts);
+}
+	
+	if(!poly.shape.sensor){
+		glColor_for_shape(cast(cpShape *)poly, space);
+		glDrawArrays(GL_TRIANGLE_FAN, 0, count);
+	}
+	
+	glColor3fv(LINE_COLOR.ptr);
+	glDrawArrays(GL_LINE_LOOP, 0, count);
+}
+
+static void
+drawObject(cpShape *shape, cpSpace *space)
+{
+	cpBody *_body = shape._body;
+	
+	switch(shape.klass.type){
+		case cpShapeType.CP_CIRCLE_SHAPE:
+			drawCircleShape(_body, cast(cpCircleShape *)shape, space);
+			break;
+		case cpShapeType.CP_SEGMENT_SHAPE:
+			drawSegmentShape(_body, cast(cpSegmentShape *)shape, space);
+			break;
+		case cpShapeType.CP_POLY_SHAPE:
+			drawPolyShape(_body, cast(cpPolyShape *)shape, space);
+			break;
+		default:
+			writefln("Bad enumeration in drawObject().");
+	}
+}
+
+enum GLfloat[] springVAR = [
+	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,
+];
+enum int springVAR_count = springVAR.length / 2;
+
+static void
+drawSpring(cpDampedSpring *spring, cpBody *body_a, cpBody *body_b)
+{
+	cpVect a = cpvadd(body_a.p, cpvrotate(spring.anchr1, body_a.rot));
+	cpVect b = cpvadd(body_b.p, cpvrotate(spring.anchr2, body_b.rot));
+
+	glPointSize(5.0f);
+	glBegin(GL_POINTS); {
+		glVertex2f(a.x, a.y);
+		glVertex2f(b.x, b.y);
+	} glEnd();
+
+	cpVect delta = cpvsub(b, a);
+
+	glVertexPointer(2, GL_FLOAT, 0, springVAR.ptr);
+	glPushMatrix(); {
+		GLfloat x = a.x;
+		GLfloat y = a.y;
+		GLfloat cos = delta.x;
+		GLfloat sin = delta.y;
+		GLfloat s = 1.0f/cpvlength(delta);
+
+		GLfloat matrix[] = [
+				 cos,    sin, 0.0f, 0.0f,
+			-sin*s,  cos*s, 0.0f, 0.0f,
+				0.0f,   0.0f, 1.0f, 0.0f,
+					 x,      y, 0.0f, 1.0f,
+		];
+		
+		glMultMatrixf(matrix.ptr);
+		glDrawArrays(GL_LINE_STRIP, 0, springVAR_count);
+	} glPopMatrix();
+}
+
+static void
+drawConstraint(cpConstraint *constraint)
+{
+	cpBody *body_a = constraint.a;
+	cpBody *body_b = constraint.b;
+
+	const cpConstraintClass *klass = constraint.klass;
+	if(klass == cpPinJointGetClass()){
+		cpPinJoint *joint = cast(cpPinJoint *)constraint;
+	
+		cpVect a = cpvadd(body_a.p, cpvrotate(joint.anchr1, body_a.rot));
+		cpVect b = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
+
+		glPointSize(5.0f);
+		glBegin(GL_POINTS); {
+			glVertex2f(a.x, a.y);
+			glVertex2f(b.x, b.y);
+		} glEnd();
+
+		glBegin(GL_LINES); {
+			glVertex2f(a.x, a.y);
+			glVertex2f(b.x, b.y);
+		} glEnd();
+	} else if(klass == cpSlideJointGetClass()){
+		cpSlideJoint *joint = cast(cpSlideJoint *)constraint;
+	
+		cpVect a = cpvadd(body_a.p, cpvrotate(joint.anchr1, body_a.rot));
+		cpVect b = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
+	
+		glPointSize(5.0f);
+		glBegin(GL_POINTS); {
+			glVertex2f(a.x, a.y);
+			glVertex2f(b.x, b.y);
+		} glEnd();
+	
+		glBegin(GL_LINES); {
+			glVertex2f(a.x, a.y);
+			glVertex2f(b.x, b.y);
+		} glEnd();
+	} else if(klass == cpPivotJointGetClass()){
+		cpPivotJoint *joint = cast(cpPivotJoint *)constraint;
+	
+		cpVect a = cpvadd(body_a.p, cpvrotate(joint.anchr1, body_a.rot));
+		cpVect b = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
+
+		glPointSize(10.0f);
+		glBegin(GL_POINTS); {
+			glVertex2f(a.x, a.y);
+			glVertex2f(b.x, b.y);
+		} glEnd();
+	} else if(klass == cpGrooveJointGetClass()){
+		cpGrooveJoint *joint = cast(cpGrooveJoint *)constraint;
+	
+		cpVect a = cpvadd(body_a.p, cpvrotate(joint.grv_a, body_a.rot));
+		cpVect b = cpvadd(body_a.p, cpvrotate(joint.grv_b, body_a.rot));
+		cpVect c = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
+	
+		glPointSize(5.0f);
+		glBegin(GL_POINTS); {
+			glVertex2f(c.x, c.y);
+		} glEnd();
+		
+		glBegin(GL_LINES); {
+			glVertex2f(a.x, a.y);
+			glVertex2f(b.x, b.y);
+		} glEnd();
+	} else if(klass == cpDampedSpringGetClass()){
+		drawSpring(cast(cpDampedSpring *)constraint, body_a, body_b);
+	} else {
+//		printf("Cannot draw constraint\n");
+	}
+}
+
+static void
+drawBB(cpShape *shape, void *unused)
+{
+	glBegin(GL_LINE_LOOP); {
+		glVertex2f(shape.bb.l, shape.bb.b);
+		glVertex2f(shape.bb.l, shape.bb.t);
+		glVertex2f(shape.bb.r, shape.bb.t);
+		glVertex2f(shape.bb.r, shape.bb.b);
+	} glEnd();
+}
+
+// copied from cpSpaceHash.c
+static cpHashValue
+hash_func(cpHashValue x, cpHashValue y, cpHashValue n)
+{
+	return cast(cpHashValue)((x*1640531513uL ^ y*2654435789uL) % n);
+}
+
+static void
+drawSpatialHash(cpSpaceHash *hash)
+{
+	cpBB bb = cpBBNew(-320, -240, 320, 240);
+	
+	cpFloat dim = hash.celldim;
+	int n = hash.numcells;
+	
+	int l = cast(int)floorf(bb.l/dim);
+	int r = cast(int)floorf(bb.r/dim);
+	int b = cast(int)floorf(bb.b/dim);
+	int t = cast(int)floorf(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 - cast(GLfloat)cell_count/10.0f;
+			glColor3f(v,v,v);
+			glRectf(i*dim, j*dim, (i + 1)*dim, (j + 1)*dim);
+		}
+	}
+}
+
+void
+DrawSpace(cpSpace *space, const drawSpaceOptions *options)
+{
+	if(options.drawHash){
+		glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE);
+		drawSpatialHash(space.activeShapes);
+		glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE);
+		drawSpatialHash(space.staticShapes);
+		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+	}
+	
+	glLineWidth(options.lineThickness);
+	if(options.drawShapes){
+		cpSpaceHashEach(space.activeShapes, cast(cpSpaceHashIterator)&drawObject, space);
+		cpSpaceHashEach(space.staticShapes, cast(cpSpaceHashIterator)&drawObject, space);
+	}
+	
+	glLineWidth(1.0f);
+	if(options.drawBBs){
+		glColor3f(0.3f, 0.5f, 0.3f);
+		cpSpaceHashEach(space.activeShapes, cast(cpSpaceHashIterator)&drawBB, null);
+		cpSpaceHashEach(space.staticShapes, cast(cpSpaceHashIterator)&drawBB, null);
+	}
+
+	cpArray *constraints = space.constraints;
+
+	glColor3f(0.5f, 1.0f, 0.5f);
+	for(int i=0, count = constraints.num; i<count; i++){
+		drawConstraint(cast(cpConstraint *)constraints.arr[i]);
+	}
+	
+	if(options.bodyPointSize){
+		glPointSize(options.bodyPointSize);
+		
+		glBegin(GL_POINTS); {
+			glColor3fv(LINE_COLOR.ptr);
+			cpArray *bodies = space.bodies;
+			for(int i=0, count = bodies.num; i<count; i++){
+				cpBody *_body = cast(cpBody *)bodies.arr[i];
+				glVertex2f(_body.p.x, _body.p.y);
+			}
+			
+//			glColor3f(0.5f, 0.5f, 0.5f);
+//			cpArray *components = space.components;
+//			for(int i=0; i<components.num; i++){
+//				cpBody *root = components.arr[i];
+//				cpBody *body = root, *next;
+//				do {
+//					next = body.node.next;
+//					glVertex2f(body.p.x, body.p.y);
+//				} while((body = next) != root);
+//			}
+		} glEnd();
+	}
+
+	if(options.collisionPointSize){
+		glPointSize(options.collisionPointSize);
+		glBegin(GL_POINTS); {
+			cpArray *arbiters = space.arbiters;
+			for(int i=0; i<arbiters.num; i++){
+				cpArbiter *arb = cast(cpArbiter*)arbiters.arr[i];
+				
+				glColor3fv(COLLISION_COLOR.ptr);
+				foreach(j; 0..arb.numContacts){
+					cpVect v = arb.contacts[j].p;
+					glVertex2f(v.x, v.y);
+				}
+			}
+		} glEnd();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/framework.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,93 @@
+
+// written in the D programming language
+
+/++
+ +	Authors: Stephan Dilly, www.extrawurst.org
+ +/
+
+module framework;
+
+import derelict.sdl.sdl;
+import derelict.opengl.gl;
+import derelict.opengl.glu;
+
+import std.string;
+import std.stdio;
+
+void startup(string _title,int _width,int _height)
+{
+	DerelictGL.load();
+	DerelictGLU.load();
+	DerelictSDL.load();
+	
+	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
+	{
+		throw new Exception("Failed to initialize SDL");
+	}
+	
+	// Enable key repeating
+	if ((SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL)))
+	{
+		throw new Exception("Failed to set key repeat");
+	}
+
+	//enable to get ascii/unicode info of key event
+	SDL_EnableUNICODE(1);
+	
+	// Set the OpenGL attributes
+	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
+	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
+	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
+	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+
+	// Set the window title
+	SDL_WM_SetCaption(cast(char*)toStringz(_title), null);
+
+	int mode = SDL_OPENGL;
+
+	// Now open a SDL OpenGL window with the given parameters
+	if (SDL_SetVideoMode(_width, _height, 32, mode) is null)
+	{
+		throw new Exception("Failed to open SDL window");
+	}
+}
+
+alias void delegate(int,int,bool) MouseButton;
+alias void delegate(int,int) MouseMove;
+alias void delegate(int,bool) KeyEvent;
+public bool processEvents(KeyEvent _keyevent,MouseMove _mmove,MouseButton _mbutton)
+{
+	SDL_Event event;
+	while (SDL_PollEvent(&event))
+	{
+		switch (event.type)
+		{
+			case SDL_KEYUP:
+			case SDL_KEYDOWN:
+				_keyevent(event.key.keysym.sym,event.type == SDL_KEYDOWN);
+				break;
+			
+			case SDL_MOUSEMOTION:
+				_mmove(event.motion.x,event.motion.y);
+				break;
+			
+			case SDL_MOUSEBUTTONUP:
+			case SDL_MOUSEBUTTONDOWN:
+				_mbutton(event.button.x,event.button.y,event.type == SDL_MOUSEBUTTONDOWN);
+				break;
+
+			case SDL_QUIT:
+				return false;
+
+			default:
+				break;
+		}
+	}
+	
+	return true;
+}
+
+void shutdown()
+{
+	SDL_Quit();
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/gameApp.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,329 @@
+/++
+ +	Authors: Stephan Dilly, www.extrawurst.org
+ +/
+
+module gameApp;
+
+import framework;
+
+import drawSpace;
+
+import chipmunkd.chipmunk;
+
+import samples.LogoSmash;
+import samples.Simple;
+import samples.PyramidStack;
+import samples.ChipmunkDemo;
+import samples.Plink;
+import samples.Tumble;
+import samples.PyramidTopple;
+import samples.Planet;
+import samples.Query;
+import samples.OneWay;
+import samples.Sensors;
+import samples.Bounce;
+import samples.Springies;
+import samples.Joints;
+import samples.MagnetsElectric;
+import samples.Player;
+import samples.Tank;
+import samples.Pump;
+import samples.TheoJansen;
+import samples.UnsafeOps;
+
+import derelict.opengl.gl;
+import derelict.opengl.glu;
+import derelict.sdl.sdl;
+
+import std.stdio;
+import core.thread:Sleep;
+
+extern(System) ulong GetTickCount();
+
+version = TIME_TRIAL;
+
+cpVect			mousePos;
+cpVect			arrowDirection;
+
+///
+final class GameApp {
+
+private:
+
+	chipmunkDemo*[] demos;
+	chipmunkDemo*	currentDemo;
+	cpSpace*		space;
+	int				ticks;
+	cpBody*			mouseBody;
+	cpConstraint*	mouseJoint;
+	cpVect			mousePos_last;
+	
+	bool key_up = false;
+	bool key_down = false;
+	bool key_left = false;
+	bool key_right = false;
+	
+	bool 	m_running = true;
+	
+	drawSpaceOptions options = {
+		0,		// drawHash
+		0,		// drawBB
+		1,		// drawShapes
+		4.0f,	// collisionPointSize
+		0.0f,	// bodyPointSize
+		1.5f,	// lineThickness
+	};
+	
+	enum width = 1024;
+	enum height = 780;
+	
+version(TIME_TRIAL)
+{
+	void time_trial(int index, int count)
+	{
+		currentDemo = demos[index];
+		space = currentDemo.initFunc();
+		
+		auto start = .GetTickCount();
+		
+		foreach(i; 0..count)
+			currentDemo.updateFunc(i);
+		
+		auto end = .GetTickCount();
+		auto duration = (end - start);
+		
+		currentDemo.destroyFunc();
+		currentDemo = null;
+		
+		writefln("Time(%s) = %s", cast(char)(index + 'a'), duration);
+	}
+}
+
+	///
+	public void boot() {
+
+		demos = [
+			&LogoSmash,
+			&Simple,
+			&PyramidStack,
+			&Plink,
+			&Tumble,
+			&PyramidTopple,
+			&Bounce,
+			&Planet,
+			&Springies,
+			&Pump,
+			&TheoJansen,
+			&MagnetsElectric,
+			&UnsafeOps,
+			&Query,
+			&OneWay,
+			&Player,
+			&Sensors,
+			&Joints,
+			&Tank,
+		];
+			
+		cpInitChipmunk();
+		
+		cp_collision_slop = 0.2f;
+		
+version(TIME_TRIAL)
+{
+		Sleep(1);
+		foreach(i; 0..demos.length)
+		{
+			//if(i == 'l' - 'a') continue;
+			time_trial(i, 1000);
+		}
+	
+		m_running = false;
+		
+		return;
+}//TIME_TRIAL
+		
+		//setup framework
+		framework.startup("chipmunk'd by Stephan Dilly",width,height);
+
+		reshape(width,height);
+				
+		glEnableClientState(GL_VERTEX_ARRAY);
+		
+		runDemo(demos[0]);
+		
+		mouseBody = cpBodyNew(INFINITY, INFINITY);
+	}
+		
+	///
+	void runDemo(chipmunkDemo *demo)
+	{
+		if(currentDemo)
+			currentDemo.destroyFunc();
+		
+		currentDemo = demo;
+		ticks = 0;
+		mouseJoint = null;
+		
+		//maxArbiters = 0;
+		//maxPoints = 0;
+		//maxConstraints = 0;
+		space = currentDemo.initFunc();
+	}
+					 
+	///
+	void reshape(int width, int height)
+	{
+		glViewport(0, 0, width, height);
+		
+		double rx = width / 2.0;
+		double ry = height / 2.0;
+		
+		glMatrixMode(GL_PROJECTION);
+		glLoadIdentity();
+		glOrtho(-rx, rx, -ry, ry, -1.0, 1.0);
+		glTranslated(0.5, 0.5, 0.0);
+	}
+
+	///
+	public bool update() {
+
+		if(!m_running) return m_running;
+		
+		if(!framework.processEvents(&keyEvent,&mouseMove,&mouseButtonEvent))
+			return false;
+					 
+		cpVect newPoint = cpvlerp(mousePos_last, mousePos, 0.25f);
+		mouseBody.p = newPoint;
+		mouseBody.v = cpvmult(cpvsub(newPoint, mousePos_last), 60.0f);
+		mousePos_last = newPoint;
+	
+		currentDemo.updateFunc(ticks++);
+		
+		// render
+		
+		glClearColor(1,1,1,1);
+
+		glClear(GL_COLOR_BUFFER_BIT);
+		
+		DrawSpace(space,currentDemo.drawOptions ? currentDemo.drawOptions : &options);
+
+		SDL_GL_SwapBuffers();
+					 
+		return m_running;
+	}
+
+	///
+	public void shutdown() {
+		
+version(TIME_TRIAL){}else
+{
+		currentDemo.destroyFunc();
+					 
+		framework.shutdown();
+}
+	}
+
+	cpVect mouseToSpace(int x, int y)
+	{
+		GLdouble model[16];
+		glGetDoublev(GL_MODELVIEW_MATRIX, model.ptr);
+	
+		GLdouble proj[16];
+		glGetDoublev(GL_PROJECTION_MATRIX, proj.ptr);
+	
+		GLint view[4];
+		glGetIntegerv(GL_VIEWPORT, view.ptr);
+	
+		GLdouble mx, my, mz;
+		gluUnProject(x, height - y, 0.0f, model.ptr, proj.ptr, view.ptr, &mx, &my, &mz);
+	
+		return cpv(mx, my);
+	}
+	
+	///
+	private void mouseMove(int x,int y)
+	{
+		mousePos = mouseToSpace(x,y);
+	}
+	
+	///
+	private void mouseButtonEvent(int x,int y,bool _down)
+	{	
+		if(_down){
+			cpVect point = mouseToSpace(x,y);
+		
+			cpShape *shape = cpSpacePointQueryFirst(space, point, GRABABLE_MASK_BIT, CP_NO_GROUP);
+			if(shape){
+				cpBody *_body = shape._body;
+				mouseJoint = cpPivotJointNew2(mouseBody, _body, cpvzero, cpBodyWorld2Local(_body, point));
+				mouseJoint.maxForce = 50000.0f;
+				mouseJoint.biasCoef = 0.15f;
+				cpSpaceAddConstraint(space, mouseJoint);
+			}
+		} else if(mouseJoint){
+			cpSpaceRemoveConstraint(space, mouseJoint);
+			cpConstraintFree(mouseJoint);
+			mouseJoint = null;
+		}
+	}
+	
+	///
+	private void set_arrowDirection()
+	{
+		int x = 0, y = 0;
+	
+		if(key_up) y += 1;
+		if(key_down) y -= 1;
+		if(key_right) x += 1;
+		if(key_left) x -= 1;
+	
+		arrowDirection = cpv(x, y);
+	}
+	
+	///
+	private void keyEvent(int _key,bool _down)
+	{				 
+		int key = _key;
+		
+		if(_down)
+		{
+			int index = key - 'a';
+			
+			if(0 <= index && index < demos.length){
+				runDemo(demos[index]);
+			} else if(key == '\r'){
+				runDemo(currentDemo);
+			} else if(key == 47){
+				options.drawHash = !options.drawHash;
+			} else if(key == 92){
+				options.drawBBs = !options.drawBBs;
+			} else if(key == 93){
+				glEnable(GL_LINE_SMOOTH);
+				glEnable(GL_POINT_SMOOTH);
+				glEnable(GL_BLEND);
+				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+				glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
+				glHint(GL_POINT_SMOOTH_HINT, GL_DONT_CARE);
+			}
+			else
+			{
+				if(key == SDLK_UP) key_up = true;
+				else if(key == SDLK_DOWN) key_down = true;
+				else if(key == SDLK_LEFT) key_left = true;
+				else if(key == SDLK_RIGHT) key_right = true;
+	
+				set_arrowDirection();
+			}
+		}
+		else
+		{
+			if(key == SDLK_UP) key_up = false;
+			else if(key == SDLK_DOWN) key_down = false;
+			else if(key == SDLK_LEFT) key_left = false;
+			else if(key == SDLK_RIGHT) key_right = false;
+			
+			set_arrowDirection();
+		}
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/main.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,39 @@
+/++
+ +	Authors: Stephan Dilly, www.extrawurst.org
+ +/
+
+module main;
+
+import gameApp;
+
+import std.stdio;
+
+///
+int main(string[] args) {
+
+	try {
+
+		scope auto game = new GameApp();
+		game.boot();
+
+		//game loop
+		while(true)
+		{
+			if(!game.update())
+				break;
+		}
+
+		game.shutdown();
+
+		writefln("bye !");
+	}
+	catch(Object o) {
+
+		//write out whatever exception is thrown
+		debug writefln("[exception] E: \"%s\"",o);
+
+		return -1;
+	}
+
+	return 0;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/Bounce.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,102 @@
+
+// written in the D programming language
+
+module samples.Bounce;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+static cpSpace *space;
+
+static void
+update(int ticks)
+{
+	enum int steps = 3;
+	enum cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	for(int i=0; i<steps; i++){
+		cpSpaceStep(space, dt);
+	}
+}
+
+static void
+add_box()
+{
+	enum cpFloat size = 10.0f;
+	enum cpFloat mass = 1.0f;
+	
+	cpVect verts[] = [
+		cpv(-size,-size),
+		cpv(-size, size),
+		cpv( size, size),
+		cpv( size,-size),
+	];
+	
+	cpFloat radius = cpvlength(cpv(size, size));
+
+	cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 4, verts.ptr, cpvzero)));
+	_body.p = cpv(frand()*(640 - 2*radius) - (320 - radius), frand()*(480 - 2*radius) - (240 - radius));
+	_body.v = cpvmult(cpv(2*frand() - 1, 2*frand() - 1), 200);
+	
+	cpShape *shape = cpSpaceAddShape(space, cpPolyShapeNew(_body, 4, verts.ptr, cpvzero));
+	shape.e = 1.0f; shape.u = 0.0f;
+}
+
+static cpSpace *
+init()
+{
+	cpResetShapeIdCounter();
+	
+	space = cpSpaceNew();
+	cpSpaceResizeActiveHash(space, 30.0f, 1000);
+	space.iterations = 10;
+
+	cpBody *_body;
+	cpBody *staticBody = &space.staticBody;
+	cpShape *shape;
+
+	// Create segments around the edge of the screen.
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,240), cpv(320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	for(int i=0; i<10; i++)
+		add_box();
+	
+	_body = cpSpaceAddBody(space, cpBodyNew(100.0f, 10000.0f));
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(_body, cpv(-75,0), cpv(75,0), 5.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	
+	cpSpaceAddConstraint(space, cpPivotJointNew2(_body, staticBody, cpvzero, cpvzero));
+	
+	return space;
+}
+
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo Bounce = {
+	"Bounce",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/ChipmunkDemo.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,33 @@
+
+// written in the D programming language
+
+module samples.ChipmunkDemo;
+
+import chipmunkd.chipmunk;
+
+import drawSpace;
+
+import core.stdc.stdlib;
+
+alias cpSpace *function() demoInitFunc;
+alias void function(int ticks) demoUpdateFunc;
+alias void function()demoDestroyFunc;
+
+struct chipmunkDemo {
+	string name;
+
+	const drawSpaceOptions *drawOptions;
+	
+	demoInitFunc	initFunc;
+	demoUpdateFunc	updateFunc;
+	demoDestroyFunc destroyFunc;
+}
+
+static cpFloat
+frand()
+{
+	return cast(cpFloat)rand()/cast(cpFloat)RAND_MAX;
+}
+
+enum GRABABLE_MASK_BIT = (1<<31);
+enum NOT_GRABABLE_MASK = (~GRABABLE_MASK_BIT);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/Joints.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,277 @@
+
+// written in the D programming language
+
+module samples.Joints;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+import std.math;
+
+static cpSpace *space;
+
+enum M_PI = PI;
+enum M_PI_2 = PI*0.5f;
+
+static cpBody *
+addBall(cpVect pos, cpVect boxOffset)
+{
+	cpFloat radius = 15.0f;
+	cpFloat mass = 1.0f;
+	cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero)));
+	_body.p = cpvadd(pos, boxOffset);
+	
+	cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(_body, radius, cpvzero));
+	shape.e = 0.0f; shape.u = 0.7f;
+	
+	return _body;
+}
+
+static cpBody *
+addLever(cpVect pos, cpVect boxOffset)
+{
+	cpFloat mass = 1.0f;
+	cpVect a = cpv(0,  15);
+	cpVect b = cpv(0, -15);
+	
+	cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForSegment(mass, a, b)));
+	_body.p = cpvadd(pos, cpvadd(boxOffset, cpv(0, -15)));
+	
+	cpShape *shape = cpSpaceAddShape(space, cpSegmentShapeNew(_body, a, b, 5.0f));
+	shape.e = 0.0f; shape.u = 0.7f;
+	
+	return _body;
+}
+
+static cpBody *
+addBar(cpVect pos, cpVect boxOffset)
+{
+	cpFloat mass = 2.0f;
+	cpVect a = cpv(0,  30);
+	cpVect b = cpv(0, -30);
+	
+	cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForSegment(mass, a, b)));
+	_body.p = cpvadd(pos, boxOffset);
+	
+	cpShape *shape = cpSpaceAddShape(space, cpSegmentShapeNew(_body, a, b, 5.0f));
+	shape.e = 0.0f; shape.u = 0.7f;
+	
+	return _body;
+}
+
+static cpBody *
+addWheel(cpVect pos, cpVect boxOffset)
+{
+	cpFloat radius = 15.0f;
+	cpFloat mass = 1.0f;
+	cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero)));
+	_body.p = cpvadd(pos, boxOffset);
+	
+	cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(_body, radius, cpvzero));
+	shape.e = 0.0f; shape.u = 0.7f;
+	shape.group = 1; // use a group to keep the car parts from colliding
+	
+	return _body;
+}
+
+static cpBody *
+addChassis(cpVect pos, cpVect boxOffset)
+{
+	int num = 4;
+	cpVect verts[] = [
+		cpv(-40,-15),
+		cpv(-40, 15),
+		cpv( 40, 15),
+		cpv( 40,-15),
+	];
+
+	
+	cpFloat mass = 5.0f;
+	cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, num, verts.ptr, cpvzero)));
+	_body.p = cpvadd(pos, boxOffset);
+	
+	cpShape *shape = cpSpaceAddShape(space, cpPolyShapeNew(_body, num, verts.ptr, cpvzero));
+	shape.e = 0.0f; shape.u = 0.7f;
+	shape.group = 1; // use a group to keep the car parts from colliding
+	
+	return _body;
+}
+
+static cpSpace *
+init()
+{
+	space = cpSpaceNew();
+	space.iterations = 10;
+	space.gravity = cpv(0, -100);
+	space.sleepTimeThreshold = 0.5f;
+	
+	cpBody *staticBody = &space.staticBody;
+	cpShape *shape;
+	
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,240), cpv(320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,120), cpv(320,120), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,0), cpv(320,0), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-120), cpv(320,-120), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-160,-240), cpv(-160,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(0,-240), cpv(0,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(160,-240), cpv(160,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	cpVect boxOffset;
+	cpBody *body1;
+	cpBody *body2;
+	
+	cpVect posA = cpv( 50, 60);
+	cpVect posB = cpv(110, 60);
+	
+	// Pin Joints - Link shapes with a solid bar or pin.
+	// Keeps the anchor points the same distance apart from when the joint was created.
+	boxOffset = cpv(-320, -240);
+	body1 = addBall(posA, boxOffset);
+	body2 = addBall(posB, boxOffset);
+	cpSpaceAddConstraint(space, cpPinJointNew(body1, body2, cpv(15,0), cpv(-15,0)));
+	
+	// Slide Joints - Like pin joints but with a min/max distance.
+	// Can be used for a cheap approximation of a rope.
+	boxOffset = cpv(-160, -240);
+	body1 = addBall(posA, boxOffset);
+	body2 = addBall(posB, boxOffset);
+	cpSpaceAddConstraint(space, cpSlideJointNew(body1, body2, cpv(15,0), cpv(-15,0), 20.0f, 40.0f));
+	
+	// Pivot Joints - Holds the two anchor points together. Like a swivel.
+	boxOffset = cpv(0, -240);
+	body1 = addBall(posA, boxOffset);
+	body2 = addBall(posB, boxOffset);
+	cpSpaceAddConstraint(space, cpPivotJointNew(body1, body2, cpvadd(boxOffset, cpv(80,60))));
+	// cpPivotJointNew() takes it's anchor parameter in world coordinates. The anchors are calculated from that
+	// cpPivotJointNew2() lets you specify the two anchor points explicitly
+	
+	// Groove Joints - Like a pivot joint, but one of the anchors is a line segment that the pivot can slide in
+	boxOffset = cpv(160, -240);
+	body1 = addBall(posA, boxOffset);
+	body2 = addBall(posB, boxOffset);
+	cpSpaceAddConstraint(space, cpGrooveJointNew(body1, body2, cpv(30,30), cpv(30,-30), cpv(-30,0)));
+	
+	// Damped Springs
+	boxOffset = cpv(-320, -120);
+	body1 = addBall(posA, boxOffset);
+	body2 = addBall(posB, boxOffset);
+	cpSpaceAddConstraint(space, cpDampedSpringNew(body1, body2, cpv(15,0), cpv(-15,0), 20.0f, 5.0f, 0.3f));
+	
+	// Damped Rotary Springs
+	boxOffset = cpv(-160, -120);
+	body1 = addBar(posA, boxOffset);
+	body2 = addBar(posB, boxOffset);
+	// Add some pin joints to hold the circles in place.
+	cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, cpvadd(boxOffset, posA)));
+	cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, cpvadd(boxOffset, posB)));
+	cpSpaceAddConstraint(space, cpDampedRotarySpringNew(body1, body2, 0.0f, 3000.0f, 60.0f));
+	
+	// Rotary Limit Joint
+	boxOffset = cpv(0, -120);
+	body1 = addLever(posA, boxOffset);
+	body2 = addLever(posB, boxOffset);
+	// Add some pin joints to hold the circles in place.
+	cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, cpvadd(boxOffset, posA)));
+	cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, cpvadd(boxOffset, posB)));
+	// Hold their rotation within 90 degrees of each other.
+	cpSpaceAddConstraint(space, cpRotaryLimitJointNew(body1, body2, -M_PI_2, M_PI_2));
+	
+	// Ratchet Joint - A rotary ratchet, like a socket wrench
+	boxOffset = cpv(160, -120);
+	body1 = addLever(posA, boxOffset);
+	body2 = addLever(posB, boxOffset);
+	// Add some pin joints to hold the circles in place.
+	cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, cpvadd(boxOffset, posA)));
+	cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, cpvadd(boxOffset, posB)));
+	// Ratchet every 90 degrees
+	cpSpaceAddConstraint(space, cpRatchetJointNew(body1, body2, 0.0f, M_PI_2));
+	
+	// Gear Joint - Maintain a specific angular velocity ratio
+	boxOffset = cpv(-320, 0);
+	body1 = addBar(posA, boxOffset);
+	body2 = addBar(posB, boxOffset);
+	// Add some pin joints to hold the circles in place.
+	cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, cpvadd(boxOffset, posA)));
+	cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, cpvadd(boxOffset, posB)));
+	// Force one to sping 2x as fast as the other
+	cpSpaceAddConstraint(space, cpGearJointNew(body1, body2, 0.0f, 2.0f));
+	
+	// Simple Motor - Maintain a specific angular relative velocity
+	boxOffset = cpv(-160, 0);
+	body1 = addBar(posA, boxOffset);
+	body2 = addBar(posB, boxOffset);
+	// Add some pin joints to hold the circles in place.
+	cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, cpvadd(boxOffset, posA)));
+	cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, cpvadd(boxOffset, posB)));
+	// Make them spin at 1/2 revolution per second in relation to each other.
+	cpSpaceAddConstraint(space, cpSimpleMotorNew(body1, body2, M_PI));
+	
+	// Make a car with some nice soft suspension
+	boxOffset = cpv(0, 0);
+	cpBody *wheel1 = addWheel(posA, boxOffset);
+	cpBody *wheel2 = addWheel(posB, boxOffset);
+	cpBody *chassis = addChassis(cpv(80, 100), boxOffset);
+	
+	cpSpaceAddConstraint(space, cpGrooveJointNew(chassis, wheel1, cpv(-30, -10), cpv(-30, -40), cpvzero));
+	cpSpaceAddConstraint(space, cpGrooveJointNew(chassis, wheel2, cpv( 30, -10), cpv( 30, -40), cpvzero));
+	
+	cpSpaceAddConstraint(space, cpDampedSpringNew(chassis, wheel1, cpv(-30, 0), cpvzero, 50.0f, 20.0f, 1.5f));
+	cpSpaceAddConstraint(space, cpDampedSpringNew(chassis, wheel2, cpv( 30, 0), cpvzero, 50.0f, 20.0f, 1.5f));
+	
+	return space;
+}
+
+static void
+update(int ticks)
+{
+	cpSpaceStep(space, 1.0f/60.0f);
+}
+
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo Joints = {
+	"Joints and Constraints",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/LogoSmash.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,135 @@
+
+// written in the D programming language
+
+module samples.LogoSmash;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+import drawSpace;
+
+enum int image_width = 188;
+enum int image_height = 35;
+enum int image_row_length = 24;
+
+enum byte[] image_bitmap = [
+	15,-16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,-64,15,63,-32,-2,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,31,-64,15,127,-125,-1,-128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,127,-64,15,127,15,-1,-64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,-1,-64,15,-2,
+	31,-1,-64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,-1,-64,0,-4,63,-1,-32,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,1,-1,-64,15,-8,127,-1,-32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	1,-1,-64,0,-8,-15,-1,-32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,-31,-1,-64,15,-8,-32,
+	-1,-32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,-15,-1,-64,9,-15,-32,-1,-32,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,31,-15,-1,-64,0,-15,-32,-1,-32,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,63,-7,-1,-64,9,-29,-32,127,-61,-16,63,15,-61,-1,-8,31,-16,15,-8,126,7,-31,
+	-8,31,-65,-7,-1,-64,9,-29,-32,0,7,-8,127,-97,-25,-1,-2,63,-8,31,-4,-1,15,-13,
+	-4,63,-1,-3,-1,-64,9,-29,-32,0,7,-8,127,-97,-25,-1,-2,63,-8,31,-4,-1,15,-13,
+	-2,63,-1,-3,-1,-64,9,-29,-32,0,7,-8,127,-97,-25,-1,-1,63,-4,63,-4,-1,15,-13,
+	-2,63,-33,-1,-1,-32,9,-25,-32,0,7,-8,127,-97,-25,-1,-1,63,-4,63,-4,-1,15,-13,
+	-1,63,-33,-1,-1,-16,9,-25,-32,0,7,-8,127,-97,-25,-1,-1,63,-4,63,-4,-1,15,-13,
+	-1,63,-49,-1,-1,-8,9,-57,-32,0,7,-8,127,-97,-25,-8,-1,63,-2,127,-4,-1,15,-13,
+	-1,-65,-49,-1,-1,-4,9,-57,-32,0,7,-8,127,-97,-25,-8,-1,63,-2,127,-4,-1,15,-13,
+	-1,-65,-57,-1,-1,-2,9,-57,-32,0,7,-8,127,-97,-25,-8,-1,63,-2,127,-4,-1,15,-13,
+	-1,-1,-57,-1,-1,-1,9,-57,-32,0,7,-1,-1,-97,-25,-8,-1,63,-1,-1,-4,-1,15,-13,-1,
+	-1,-61,-1,-1,-1,-119,-57,-32,0,7,-1,-1,-97,-25,-8,-1,63,-1,-1,-4,-1,15,-13,-1,
+	-1,-61,-1,-1,-1,-55,-49,-32,0,7,-1,-1,-97,-25,-8,-1,63,-1,-1,-4,-1,15,-13,-1,
+	-1,-63,-1,-1,-1,-23,-49,-32,127,-57,-1,-1,-97,-25,-1,-1,63,-1,-1,-4,-1,15,-13,
+	-1,-1,-63,-1,-1,-1,-16,-49,-32,-1,-25,-1,-1,-97,-25,-1,-1,63,-33,-5,-4,-1,15,
+	-13,-1,-1,-64,-1,-9,-1,-7,-49,-32,-1,-25,-8,127,-97,-25,-1,-1,63,-33,-5,-4,-1,
+	15,-13,-1,-1,-64,-1,-13,-1,-32,-49,-32,-1,-25,-8,127,-97,-25,-1,-2,63,-49,-13,
+	-4,-1,15,-13,-1,-1,-64,127,-7,-1,-119,-17,-15,-1,-25,-8,127,-97,-25,-1,-2,63,
+	-49,-13,-4,-1,15,-13,-3,-1,-64,127,-8,-2,15,-17,-1,-1,-25,-8,127,-97,-25,-1,
+	-8,63,-49,-13,-4,-1,15,-13,-3,-1,-64,63,-4,120,0,-17,-1,-1,-25,-8,127,-97,-25,
+	-8,0,63,-57,-29,-4,-1,15,-13,-4,-1,-64,63,-4,0,15,-17,-1,-1,-25,-8,127,-97,
+	-25,-8,0,63,-57,-29,-4,-1,-1,-13,-4,-1,-64,31,-2,0,0,103,-1,-1,-57,-8,127,-97,
+	-25,-8,0,63,-57,-29,-4,-1,-1,-13,-4,127,-64,31,-2,0,15,103,-1,-1,-57,-8,127,
+	-97,-25,-8,0,63,-61,-61,-4,127,-1,-29,-4,127,-64,15,-8,0,0,55,-1,-1,-121,-8,
+	127,-97,-25,-8,0,63,-61,-61,-4,127,-1,-29,-4,63,-64,15,-32,0,0,23,-1,-2,3,-16,
+	63,15,-61,-16,0,31,-127,-127,-8,31,-1,-127,-8,31,-128,7,-128,0,0
+];
+
+static int
+get_pixel(int x, int y)
+{
+	return (image_bitmap[(x>>3) + y*image_row_length]>>(~x&0x7)) & 1;
+}
+
+static cpSpace *space;
+
+static void
+update(int ticks)
+{
+	enum int steps = 1;
+	enum cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	for(int i=0; i<steps; i++){
+		cpSpaceStep(space, dt);
+	}
+}
+
+static cpShape *
+make_ball(cpFloat x, cpFloat y)
+{
+	cpBody *_body = cpBodyNew(1.0f, INFINITY);
+	_body.p = cpv(x, y);
+
+	cpShape *shape = cpCircleShapeNew(_body, 0.95f, cpvzero);
+	shape.e = 0.0f; shape.u = 0.0f;
+	
+	return shape;
+}
+
+static cpSpace *
+init()
+{
+	space = cpSpaceNew();
+	cpSpaceResizeActiveHash(space, 2.0f, 10000);
+	cpSpaceResizeStaticHash(space, 2.0f, 10000);
+	space.iterations = 1;
+	
+	cpBody *_body;
+	cpShape *shape;
+	
+	for(int y=0; y<image_height; y++){
+		for(int x=0; x<image_width; x++){
+			if(!get_pixel(x, y)) continue;
+			
+			cpFloat x_jitter = 0.05f*frand();
+			cpFloat y_jitter = 0.05f*frand();
+			
+			shape = make_ball(2*(x - image_width/2 + x_jitter), 2*(image_height/2 - y + y_jitter));
+			cpSpaceAddBody(space, shape._body);
+			cpSpaceAddShape(space, shape);
+		}
+	}
+	
+	_body = cpSpaceAddBody(space, cpBodyNew(INFINITY, INFINITY));
+	_body.p = cpv(-1000.0f, -10.0f);
+	_body.v = cpv(400.0f, 0.0f);
+
+	shape = cpSpaceAddShape(space, cpCircleShapeNew(_body, 8.0f, cpvzero));
+	shape.e = 0.0f; shape.u = 0.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	return space;
+}
+
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+static const drawSpaceOptions draw_options = {
+	0, 0, 0, 2.0f, 3.0f, 0.0f,
+};
+
+chipmunkDemo LogoSmash = {
+	"Logo Smash",
+	&draw_options,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/MagnetsElectric.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,503 @@
+
+// written in the D programming language
+
+module samples.MagnetsElectric;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+import std.math;
+import core.stdc.string:strcmp;
+import core.stdc.stdio:sprintf;
+
+enum WIDTH = 600;
+enum HEIGHT = 400;
+
+enum SINGMAX = 10; // Maximum number of singularities per body
+enum NMAG = 10; // Number of magnets
+enum NCHG = 10; // Number of charged bodies
+enum NMIX = 10; // Number of charged magnets
+
+enum COU_MKS = 8.987551787e9; // Some physical constants
+enum MAG_MKS = 1e-7;
+
+// Prototypes
+alias void function(DataforForce* data)SingForceFunc;
+
+// Structures
+// Singularities
+struct ActorSingularity{
+	// Number of singularities 
+	int Nsing; 
+	// Value of the singularities 
+	cpFloat value[SINGMAX];
+	// Type of the singularities 
+	char type[SINGMAX][100]; 
+	// Global position of the singularities
+	cpVect Gpos[SINGMAX];
+	// Local position of the singularities
+	cpVect position[SINGMAX];
+	// Angle of the singularities measured in the body axes
+	cpFloat angle[SINGMAX];
+	// Angle of the singularities measured from x
+	cpFloat Gangle[SINGMAX];
+	// Force function
+	SingForceFunc force_func[SINGMAX];
+	// Force function
+	SingForceFunc torque_func[SINGMAX];
+}
+alias ActorSingularity Sing;
+
+// Data for the force functions
+struct DataforForce{
+	//Everything in global coordinates
+	// Position of the source
+	cpVect p0;
+	// Observed position
+	cpVect p;
+	// Relative position source-observed
+	cpVect relp;
+	// distance, disntace^2, ditance ^3
+	cpFloat r[3];
+	// angle of the source
+	cpFloat ang0;
+	// angle of the observed singularity
+	cpFloat ang;
+	// Foce value
+	cpVect F;
+	// Torque value
+	cpFloat T;
+}
+alias DataforForce ForceData;
+
+// Global Varibales
+static cpSpace *space;
+
+
+// **** Forces ****** // 
+// Calculate the forces between two bodies. all this functions requieres 
+// a pointer to an structure with the necessary fields.
+
+// forces between charges
+static void
+CoulombForce(ForceData* data){
+	data.F=cpvmult(cpvnormalize(data.relp),COU_MKS/data.r[1]);
+}
+
+// forces between magnets
+static void
+MagDipoleForce(ForceData* data){
+	static cpFloat phi,alpha,beta,Fr,Fphi;
+	
+	// Angle of the relative position vector
+	phi=cpvtoangle(data.relp);
+	alpha=data.ang0;
+	beta=data.ang;		
+
+	alpha =phi - alpha;
+	beta = phi - beta;
+	
+	
+	// Components in polar coordinates
+	Fr=(2.0e0*cos(alpha)*cos(beta) - sin(alpha)*sin(beta));
+	Fphi=sin(alpha+beta);
+//	printf("%g %g %g %g %g\n",phi,alpha,beta,Fphi);
+	
+	// Cartesian coordinates
+	data.F=cpv(Fr*cos(phi)-Fphi*sin(phi),Fr*sin(phi)+Fphi*cos(phi));
+	data.F=cpvmult(data.F,-3.e0*MAG_MKS/(data.r[1]*data.r[1]));
+}
+
+static void
+MagDipoleTorque(ForceData* data){
+	static cpFloat phi,alpha,beta;
+	
+	phi=cpvtoangle(data.relp);
+	alpha=data.ang0;
+	beta=data.ang;		
+	alpha =phi - alpha;
+	beta = phi - beta;
+
+	// Torque. Though we could use a component of F to save some space, 
+	// we use another variables for the sake of clarity.
+	
+	data.T=(MAG_MKS/data.r[2])*(3.0e0*cos(alpha)*sin(beta) + sin(alpha-beta));
+}
+// ******* // 
+
+// This function fills the data structure for the force functions
+// The structure Sing has the information about the singularity (charge or magnet)
+static void
+FillForceData(Sing* source,int inds, Sing* obs,int indo, ForceData* data)
+{
+	// Global Position and orientation of the source singularity
+	 data.p0=source.Gpos[inds];
+	 data.ang0=source.Gangle[inds]; 
+	 
+	// Global Position and orientation of the observed singularity
+	 data.p=obs.Gpos[indo];
+	 data.ang=obs.Gangle[indo];
+	
+	// Derived magnitudes
+	 data.relp=cpvsub(data.p,data.p0); //Relative position
+	 data.r[0]=cpvlength(data.relp); // Distance
+	 data.r[1]=cpvlengthsq(data.relp); // Square Distance
+	 data.r[2]=data.r[0]*data.r[1]; // Cubic distance
+	 
+     source.force_func[inds](data); // The value of the force
+	 data.F= cpvmult(data.F,source.value[inds]*obs.value[indo]);
+}
+
+// Calculation of the interaction
+static void
+LRangeForceApply(cpBody *a, cpBody *b){
+	
+	Sing* aux = cast(Sing*)a.data;
+	Sing* aux2 = cast(Sing*)b.data;
+	cpVect delta;
+	// General data needed to calculate interaction
+	static ForceData fdata;
+	fdata.F=cpvzero;
+	
+	// Calculate the forces between the charges of different bodies
+	for (int i=0; i<aux.Nsing; i++)
+	{
+		for (int j=0; j<aux2.Nsing; j++)
+		{
+			if(!strcmp(aux.type[i].ptr,aux2.type[j].ptr))
+			{
+				//printf("%s %s\n",aux.type[i],aux2.type[j]);
+				FillForceData (aux2,j,aux,i,&fdata);
+				
+				//Force applied to body A
+				delta=cpvsub(aux.Gpos[i], a.p);
+				cpBodyApplyForce(a,fdata.F, delta);
+				
+	 			if(aux.torque_func[i] != null)
+				{
+					//Torque on A
+					aux.torque_func[i](&fdata);
+					a.t += aux.value[i]*aux2.value[j]*fdata.T;
+					
+				}
+			}
+		}
+	}
+}
+
+// function for the integration of the positions
+// The following functions are variations to the starndrd integration in Chipmunk
+// you can go ack to the standard ones by doing the appropiate changes.
+static void
+ChargedBodyUpdatePositionVerlet(cpBody *_body, cpFloat dt)
+{
+    // Long range interaction
+    cpArray *bodies = space.bodies;
+	static cpBody* B;
+	Sing* aux=cast(Sing*)_body.data;
+	Sing* aux2;
+
+	// General data needed to calculate interaction
+	static ForceData fdata;
+	fdata.F=cpvzero;
+	
+	for(int i=0; i< bodies.num; i++)
+	{
+	  B=cast(cpBody*)bodies.arr[i];
+	  aux2=cast(Sing*)B.data;
+	  
+	  if(B != _body)
+	  {
+        // Calculate the forces between the singularities of different bodies
+        LRangeForceApply(_body, B);
+	  }
+	}
+	
+	cpVect dp = cpvmult(cpvadd(_body.v, _body.v_bias), dt);
+	dp = cpvadd(dp,cpvmult(cpvmult(_body.f, _body.m_inv), 0.5e0*dt*dt));
+	_body.p = cpvadd(_body.p, dp);
+
+	cpBodySetAngle(_body, cast(float)(_body.a + (_body.w + _body.w_bias)*dt 
+				   + 0.5*_body.t*_body.i_inv*dt*dt));
+
+	// Update position of the singularities
+	aux = cast(Sing*)_body.data;
+	for (int i=0; i<aux.Nsing; i++)
+	{
+        aux.Gpos[i]=cpvadd(_body.p,cpvrotate(cpv(aux.position[i].x,
+										  aux.position[i].y), _body.rot));
+		aux.Gangle[i]= aux.angle[i] + _body.a;
+	}
+	
+            
+ 	_body.v_bias = cpvzero;
+	_body.w_bias = 0.0f;
+}
+
+// function for the integration of the velocities
+static void
+ChargedBodyUpdateVelocityVerlet(cpBody *_body, cpVect gravity, cpFloat damping, cpFloat dt)
+{
+	_body.v = cpvadd(_body.v, cpvmult(cpvadd(gravity, cpvmult(_body.f, _body.m_inv)), 0.5e0*dt));
+	_body.w = _body.w + _body.t*_body.i_inv*0.5e0*dt;
+	
+	_body.f = cpvzero;
+	_body.t = 0.0e0;
+	
+	// Long range interaction
+    cpArray *bodies = space.bodies;
+	static cpBody* B;
+
+	// General data needed to calculate interaction
+	static ForceData fdata;
+	fdata.F=cpvzero;
+	
+	for(int i=0; i< bodies.num; i++)
+	{
+	  B=cast(cpBody*)bodies.arr[i];
+	  
+	  if(B != _body)
+	  {
+        // Calculate the forces between the singularities of different bodies
+        LRangeForceApply(_body, B);
+	  }
+	}
+	_body.v = cpvadd(cpvmult(_body.v,damping), cpvmult(cpvadd(gravity, cpvmult(_body.f, _body.m_inv)), 0.5e0*dt));
+	_body.w = _body.w*damping + _body.t*_body.i_inv*0.5e0*dt;
+}
+
+static void 
+update(int ticks)
+{
+	enum int steps = 10;
+	enum cpFloat dt = 1.0/60.0/cast(cpFloat)steps;
+	
+	cpArray *bodies = space.bodies;
+
+	for(int i=0; i< bodies.num; i++)
+		cpBodyResetForces(cast(cpBody*)bodies.arr[i]);
+	
+	for(int i=0; i<steps; i++){
+		cpSpaceStep(space, dt);
+	}
+	
+}
+
+static void
+make_mag(cpVect p, cpFloat ang, cpFloat mag)
+{
+	int nverts=6;
+	cpVect verts[] = [
+		cpv(-10,-10),
+		cpv(-10, 10),
+		cpv( 10, 10),
+		cpv( 15, 5),
+		cpv( 15, -5),
+		cpv( 10,-10)
+	];
+
+	cpBody *_body = cpBodyNew(1.0, cpMomentForPoly(1.0f, nverts, verts.ptr, cpvzero));
+	_body.p = p;
+	_body.v = cpvzero;
+	cpBodySetAngle(_body, ang);
+	_body.w = 0.0e0;
+	
+    // Load the singularities
+    Sing *magnet=cast(Sing*)cpmalloc(Sing.sizeof);
+	magnet.Nsing=1; 
+	magnet.value[0]=mag;
+	sprintf(magnet.type[0].ptr,"magdipole"); 
+
+	// The position and angle could be different form the one of the body
+	magnet.position[0]=cpvzero;
+	magnet.Gpos[0]=cpvadd(p,magnet.position[0]);
+	magnet.angle[0]=0.0f;
+	magnet.Gangle[0]=ang;
+	
+	magnet.force_func[0]=&MagDipoleForce;
+	magnet.torque_func[0]=&MagDipoleTorque;
+
+	_body.data=magnet;
+	
+    _body.position_func=&ChargedBodyUpdatePositionVerlet;
+    _body.velocity_func=&ChargedBodyUpdateVelocityVerlet;
+	cpSpaceAddBody(space, _body);
+	
+	cpShape *shape = cpPolyShapeNew(_body, nverts, verts.ptr, cpvzero);
+	shape.e = 0.0; shape.u = 0.7;
+	cpSpaceAddShape(space, shape);
+}
+
+static void
+make_charged(cpVect p, cpFloat chg)
+{
+	int nverts=4;
+	cpVect verts[] = [
+		cpv(-10,-10),
+		cpv(-10, 10),
+		cpv( 10, 10),
+		cpv( 10,-10)
+	];
+
+	cpBody *_body = cpBodyNew(1.0, cpMomentForPoly(1.0, nverts, verts.ptr, cpvzero));
+	_body.p = p;
+	_body.v = cpvzero;
+	cpBodySetAngle(_body, 0.0f);
+	_body.w = 0.0e0;
+	
+    // Load the singularities
+    Sing *charge=cast(Sing*)cpmalloc(Sing.sizeof);;
+	charge.Nsing=1; 
+	charge.value[0]=chg;
+	sprintf(charge.type[0].ptr,"electrical\0"); 
+
+	// The position and angle could be different form the one of the body
+	charge.position[0]=cpvzero;
+	charge.Gpos[0]=cpvadd(p,charge.position[0]);
+	charge.Gangle[0]=0;
+	
+	charge.force_func[0]=&CoulombForce;
+	charge.torque_func[0]=null;
+	
+	_body.data=charge;
+	
+    _body.position_func=&ChargedBodyUpdatePositionVerlet;
+    _body.velocity_func=&ChargedBodyUpdateVelocityVerlet;
+	cpSpaceAddBody(space, _body);
+	
+	cpShape *shape = cpPolyShapeNew(_body, nverts, verts.ptr, cpvzero);
+	shape.e = 0.0; shape.u = 0.7;
+	cpSpaceAddShape(space, shape);
+}
+void 
+make_mix(cpVect p, cpFloat ang, cpFloat mag,cpFloat chg)
+{
+	int nverts=5;
+	cpVect verts[] = [
+		cpv(-10,-10),
+		cpv(-10, 10),
+		cpv( 10, 10),
+		cpv( 20, 0),		
+		cpv( 10,-10)
+	];
+
+	cpBody *_body = cpBodyNew(1.0, cpMomentForPoly(1.0, nverts, verts.ptr, cpvzero));
+	_body.p = p;
+	_body.v = cpvzero;
+	cpBodySetAngle(_body, ang);
+	_body.w = 0.0e0;
+	
+    // Load the singularities
+    Sing *mix=cast(Sing*)cpmalloc(Sing.sizeof);;
+	mix.Nsing=2; 
+	mix.value[0]=mag;
+	mix.value[1]=chg;
+	sprintf(mix.type[0].ptr,"magdipole\0");
+	sprintf(mix.type[1].ptr,"electrical\0");  
+
+	// The position and angle could be different form the one of the body
+	mix.position[0]=cpvzero;
+	mix.Gpos[0]=cpvadd(p,mix.position[0]);
+	mix.position[1]=cpvzero;
+	mix.Gpos[1]=cpvadd(p,mix.position[1]);
+	mix.Gangle[0]=ang;
+	mix.Gangle[1]=ang;	
+	
+	mix.force_func[0]=&MagDipoleForce;
+	mix.force_func[1]=&CoulombForce;
+	mix.torque_func[0]=&MagDipoleTorque;
+	mix.torque_func[1]=null;	
+	
+	_body.data=mix;
+	
+    _body.position_func=&ChargedBodyUpdatePositionVerlet;
+    _body.velocity_func=&ChargedBodyUpdateVelocityVerlet;
+	cpSpaceAddBody(space, _body);
+	
+	cpShape *shape = cpPolyShapeNew(_body, nverts, verts.ptr, cpvzero);
+	shape.e = 0.0; shape.u = 0.7;
+	cpSpaceAddShape(space, shape);
+}
+
+
+static cpSpace* 
+init()
+{
+	cpResetShapeIdCounter();
+	space = cpSpaceNew();
+	space.iterations = 5;
+	space.gravity = cpvzero; //cpv(0,-100);
+	
+	cpSpaceResizeActiveHash(space, 30.0, 2999);
+
+	// Screen border
+/*	shape = cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f);
+	shape.e = 1.0; shape.u = 1.0;
+	cpSpaceAddShape(space, shape);
+
+	shape = cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f);
+	shape.e = 1.0; shape.u = 1.0;
+	cpSpaceAddShape(space, shape);
+
+	shape = cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f);
+	shape.e = 1.0; shape.u = 1.0;
+	cpSpaceAddShape(space, shape);
+
+	// Reference line
+	// Does not collide with other objects, we just want to draw it.
+	shape = cpSegmentShapeNew(staticBody, cpv(-320,0), cpv(320,0), 0.0f);
+	shape.collision_type = 1;
+	cpSpaceAddShape(space, shape);
+	// Add a collision pair function to filter collisions
+	cpSpaceAddCollisionPairFunc(space, 0, 1, null, null);
+*/		
+	
+	//srand(cast(uint) time(null));
+    cpVect p;
+	cpFloat ang;
+	
+	// Create magnets
+	for(int i=0; i<NMAG; i++)
+	{
+	  p.x=(2.0e0*frand() - 1.0e0)*WIDTH/2.0f;
+  	  p.y=(2.0e0*frand() - 1.0e0)*HEIGHT/2.0f;
+  	  ang=(2.0e0*frand() - 1.0e0)*3.1415;
+	  make_mag(p, ang,1.0e7);
+	}
+	
+	// Create charged objects
+	for(int i=0; i<NCHG; i++)
+	{
+	  p.x=(2.0e0*frand() - 1.0e0)*WIDTH/2.0f;
+  	  p.y=(2.0e0*frand() - 1.0e0)*HEIGHT/2.0f;
+  	  ang=(2.0e0*frand() - 1.0e0)*3.1415;
+	  make_charged(p,1.0e-3*pow(-1.0,i%2));
+	}
+		
+	// Create charged magnets objects
+	for(int i=0; i<NMIX; i++)
+	{
+      p.x=(2.0e0*frand() - 1.0e0)*WIDTH/2.0f;
+  	  p.y=(2.0e0*frand() - 1.0e0)*HEIGHT/2.0f;
+  	  ang=(2.0e0*frand() - 1.0e0)*3.1415;
+	  make_mix(p, ang,1.0e7*pow(-1.0,i%2), 1.0e-3*pow(-1.0,i%2));
+	}
+	
+	return space;
+}
+
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo MagnetsElectric = {
+	"Magnets and Electric Charges (By: Juan Pablo Carbajal)",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/OneWay.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,112 @@
+
+// written in the D programming language
+
+module samples.OneWay;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+static cpSpace *space;
+
+struct OneWayPlatform {
+	cpVect n; // direction objects may pass through
+	cpArray *passThruList; // list of objects passing through
+}
+
+static OneWayPlatform platformInstance;
+
+static cpBool
+preSolve(cpArbiter *arb, cpSpace *space, void *ignore)
+{
+	mixin(CP_ARBITER_GET_SHAPES!("arb", "a", "b"));
+	OneWayPlatform *platform = cast(OneWayPlatform *)a.data;
+		
+	if(cpvdot(cpArbiterGetNormal(arb, 0), platform.n) < 0){
+		cpArbiterIgnore(arb);
+		return cpFalse;
+	}
+	
+	return cpTrue;
+}
+
+static void
+update(int ticks)
+{
+	int steps = 1;
+	cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	for(int i=0; i<steps; i++){
+		cpSpaceStep(space, dt);
+	}
+}
+
+static cpSpace *
+init()
+{
+	cpResetShapeIdCounter();
+	
+	space = cpSpaceNew();
+	space.iterations = 10;
+	space.gravity = cpv(0, -100);
+
+	cpBody *_body;
+	cpBody *staticBody = &space.staticBody;
+	cpShape *shape;
+
+	// Create segments around the edge of the screen.
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	// Add our one way segment
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-160,-100), cpv(160,-100), 10.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.collision_type = 1;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	// We'll use the data pointer for the OneWayPlatform struct
+	platformInstance.n = cpv(0, 1); // let objects pass upwards
+	platformInstance.passThruList = cpArrayNew(0);
+	shape.data = &platformInstance;
+	
+	
+	// Add a ball to make things more interesting
+	cpFloat radius = 15.0f;
+	_body = cpSpaceAddBody(space, cpBodyNew(10.0f, cpMomentForCircle(10.0f, 0.0f, radius, cpvzero)));
+	_body.p = cpv(0, -200);
+	_body.v = cpv(0, 170);
+
+	shape = cpSpaceAddShape(space, cpCircleShapeNew(_body, radius, cpvzero));
+	shape.e = 0.0f; shape.u = 0.9f;
+	shape.collision_type = 2;
+	
+	cpSpaceAddCollisionHandler(space, 1, 2, null, &preSolve, null, null, null);
+	
+	return space;
+}
+
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+	
+	cpArrayFree(platformInstance.passThruList);
+}
+
+chipmunkDemo OneWay = {
+	"One Way Platforms",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/Planet.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,123 @@
+
+// written in the D programming language
+
+module samples.Planet;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+static cpSpace *space;
+static cpBody *planetBody;
+
+static cpFloat gravityStrength = 5.0e6f;
+
+static void
+update(int ticks)
+{
+	int steps = 1;
+	cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	for(int i=0; i<steps; i++){
+		cpSpaceStep(space, dt);
+		
+		// Update the static body spin so that it looks like it's rotating.
+		cpBodyUpdatePosition(planetBody, dt);
+	}
+}
+
+static void
+planetGravityVelocityFunc(cpBody *_body, cpVect gravity, cpFloat damping, cpFloat dt)
+{
+	// Gravitational acceleration is proportional to the inverse square of
+	// distance, and directed toward the origin. The central planet is assumed
+	// to be massive enough that it affects the satellites but not vice versa.
+	cpVect p = _body.p;
+	cpFloat sqdist = cpvlengthsq(p);
+	cpVect g = cpvmult(p, -gravityStrength / (sqdist * cpfsqrt(sqdist)));
+	
+	cpBodyUpdateVelocity(_body, g, damping, dt);
+}
+
+static cpVect
+rand_pos(cpFloat radius)
+{
+	cpVect v;
+	do {
+		v = cpv(frand()*(640 - 2*radius) - (320 - radius), frand()*(480 - 2*radius) - (240 - radius));
+	} while(cpvlength(v) < 85.0f);
+	
+	return v;
+}
+
+static void
+add_box()
+{
+	const cpFloat size = 10.0f;
+	const cpFloat mass = 1.0f;
+	
+	cpVect verts[] = [
+		cpv(-size,-size),
+		cpv(-size, size),
+		cpv( size, size),
+		cpv( size,-size),
+	];
+	
+	cpFloat radius = cpvlength(cpv(size, size));
+
+	cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 4, verts.ptr, cpvzero)));
+	_body.velocity_func = &planetGravityVelocityFunc;
+	_body.p = rand_pos(radius);
+
+	// Set the box's velocity to put it into a circular orbit from its
+	// starting position.
+	cpFloat r = cpvlength(_body.p);
+	cpFloat v = cpfsqrt(gravityStrength / r) / r;
+	_body.v = cpvmult(cpvperp(_body.p), v);
+
+	// Set the box's angular velocity to match its orbital period and
+	// align its initial angle with its position.
+	_body.w = v;
+	cpBodySetAngle(_body, cpfatan2(_body.p.y, _body.p.x));
+
+	cpShape *shape = cpSpaceAddShape(space, cpPolyShapeNew(_body, 4, verts.ptr, cpvzero));
+	shape.e = 0.0f; shape.u = 0.7f;
+}
+
+static cpSpace *
+init()
+{
+	planetBody = cpBodyNew(INFINITY, INFINITY);
+	planetBody.w = 0.2f;
+	
+	cpResetShapeIdCounter();
+	
+	space = cpSpaceNew();
+	cpSpaceResizeActiveHash(space, 30.0f, 10000);
+	space.iterations = 20;
+	
+	for(int i=0; i<30; i++)
+		add_box();
+	
+	cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(planetBody, 70.0f, cpvzero));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	return space;
+}
+
+static void
+destroy()
+{
+	cpBodyFree(planetBody);
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo Planet = {
+	"Planet",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/Player.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,208 @@
+
+// written in the D programming language
+
+module samples.Player;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+import gameApp;
+
+static cpSpace *space;
+
+struct PlayerStruct {
+	cpFloat u;
+	cpShape *shape;
+	cpVect groundNormal;
+	cpArray *groundShapes;
+}
+
+PlayerStruct playerInstance;
+
+static cpBool
+begin(cpArbiter *arb, cpSpace *space, void *ignore)
+{
+	mixin(CP_ARBITER_GET_SHAPES!("arb", "a", "b"));
+	PlayerStruct *player = cast(PlayerStruct *)a.data;
+	
+	cpVect n = cpvneg(cpArbiterGetNormal(arb, 0));
+	if(n.y > 0.0f){
+		cpArrayPush(player.groundShapes, b);
+	}
+	
+	return cpTrue;
+}
+
+static cpBool
+preSolve(cpArbiter *arb, cpSpace *space, void *ignore)
+{
+	mixin(CP_ARBITER_GET_SHAPES!("arb", "a", "b"));
+	PlayerStruct *player = cast(PlayerStruct *)a.data;
+	
+	if(cpArbiterIsFirstContact(arb)){
+		a.u = player.u;
+		
+		// pick the most upright jump normal each frame
+		cpVect n = cpvneg(cpArbiterGetNormal(arb, 0));
+		if(n.y >= player.groundNormal.y){
+			player.groundNormal = n;
+		}
+	}
+	
+	return cpTrue;
+}
+
+static void
+separate(cpArbiter *arb, cpSpace *space, void *ignore)
+{
+	mixin(CP_ARBITER_GET_SHAPES!("arb", "a", "b"));
+	PlayerStruct *player = cast(PlayerStruct *)a.data;
+	
+	cpArrayDeleteObj(player.groundShapes, b);
+	
+	if(player.groundShapes.num == 0){
+		a.u = 0.0f;
+		player.groundNormal = cpvzero;
+	}
+}
+
+static void
+playerUpdateVelocity(cpBody *_body, cpVect gravity, cpFloat damping, cpFloat dt)
+{
+	cpBodyUpdateVelocity(_body, gravity, damping, dt);
+	_body.v.y = cpfmax(_body.v.y, -700);
+	_body.v.x = cpfclamp(_body.v.x, -400, 400);
+}
+
+
+static void
+update(int ticks)
+{
+	static int lastJumpState = 0;
+	int jumpState = (arrowDirection.y > 0.0f);
+	
+	cpBody *_body = playerInstance.shape._body;
+	
+	cpVect groundNormal = playerInstance.groundNormal;
+	if(groundNormal.y > 0.0f){
+		playerInstance.shape.surface_v = cpvmult(cpvperp(groundNormal), 400.0f*arrowDirection.x);
+		if(arrowDirection.x) cpBodyActivate(_body);
+	} else {
+		playerInstance.shape.surface_v = cpvzero;
+	}
+	
+	// apply jump
+	if(jumpState && !lastJumpState && cpvlengthsq(groundNormal)){
+//		body.v = cpvmult(cpvslerp(groundNormal, cpv(0.0f, 1.0f), 0.5f), 500.0f);
+		_body.v = cpvadd(_body.v, cpvmult(cpvslerp(groundNormal, cpv(0.0f, 1.0f), 0.75f), 500.0f));
+		cpBodyActivate(_body);
+	}
+	
+	if(playerInstance.groundShapes.num == 0){
+		cpFloat air_accel = _body.v.x + arrowDirection.x*(2000.0f);
+		_body.f.x = _body.m*air_accel;
+//		body.v.x = cpflerpconst(body.v.x, 400.0f*arrowDirection.x, 2000.0f/60.0f);
+	}
+	
+	enum int steps = 3;
+	enum cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	for(int i=0; i<steps; i++){
+		cpSpaceStep(space, dt);
+	}
+	
+	lastJumpState = jumpState;
+}
+
+static cpSpace *
+init()
+{
+	cpResetShapeIdCounter();
+	
+	space = cpSpaceNew();
+	space.iterations = 10;
+	space.gravity = cpv(0, -1500);
+
+	cpBody *_body;
+	cpBody *staticBody = &space.staticBody;
+	cpShape *shape;
+	
+	// Create segments around the edge of the screen.
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	shape.collision_type = 2;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	shape.collision_type = 2;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	shape.collision_type = 2;
+	
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,240), cpv(320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	shape.collision_type = 2;
+	
+	// add some other segments to play with
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-220,-200), cpv(-220,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	shape.collision_type = 2;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(0,-240), cpv(320,-200), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	shape.collision_type = 2;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(200,-240), cpv(320,-100), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	shape.collision_type = 2;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-220,-80), cpv(200,-80), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	shape.collision_type = 2;
+	
+	// Set up the player
+	cpFloat radius = 15.0f;
+	_body = cpSpaceAddBody(space, cpBodyNew(10.0f, INFINITY));
+	_body.p = cpv(0, -220);
+	_body.velocity_func = &playerUpdateVelocity;
+
+	shape = cpSpaceAddShape(space, cpCircleShapeNew(_body, radius, cpvzero));
+	shape.e = 0.0f; shape.u = 2.0f;
+	shape.collision_type = 1;
+	
+	playerInstance.u = shape.u;
+	playerInstance.shape = shape;
+	playerInstance.groundShapes = cpArrayNew(0);
+	shape.data = &playerInstance;
+	
+	cpSpaceAddCollisionHandler(space, 1, 2, &begin, &preSolve, null, &separate, null);
+	
+	return space;
+}
+
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+	
+	cpArrayFree(playerInstance.groundShapes);
+}
+
+chipmunkDemo Player = {
+	"Player",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/Plink.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,112 @@
+
+// written in the D programming language
+
+module samples.Plink;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+import std.math;
+
+static cpSpace *space;
+
+// Iterate over all of the bodies and reset the ones that have fallen offscreen.
+static void
+eachBody(cpBody *_body, void *unused)
+{
+	if(_body.p.y < -260 || cpfabs(_body.p.x) > 340){
+		cpFloat x = frand()*640 - 320;
+		_body.p = cpv(x, 260);
+	}
+}
+
+static void
+update(int ticks)
+{
+	int steps = 1;
+	cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	for(int i=0; i<steps; i++){
+		cpSpaceStep(space, dt);
+		cpSpaceEachBody(space, &eachBody, null);
+	}
+}
+
+enum NUM_VERTS = 5;
+
+static cpSpace *
+init()
+{
+	cpResetShapeIdCounter();
+	
+	space = cpSpaceNew();
+	space.iterations = 5;
+	space.gravity = cpv(0, -100);
+	
+	cpSpaceResizeStaticHash(space, 40.0f, 999);
+	cpSpaceResizeActiveHash(space, 30.0f, 2999);
+	
+	cpBody *_body;
+	cpBody *staticBody = &space.staticBody;
+	cpShape *shape;
+	
+	// Create vertexes for a pentagon shape.
+	cpVect verts[NUM_VERTS];
+	for(int i=0; i<NUM_VERTS; i++){
+		cpFloat angle = -2.0f*PI*i/(cast(cpFloat) NUM_VERTS);
+		verts[i] = cpv(10.0f*cos(angle), 10.0f*sin(angle));
+	}
+	
+	// Vertexes for a triangle shape.
+	enum cpVect tris[] = [
+		cpv(-15,-15),
+		cpv(  0, 10),
+		cpv( 15,-15),
+	];
+
+	int foo;
+	
+	// Create the static triangles.
+	foreach(i; 0..9){
+		foreach(j; 0..6){
+			cpFloat stagger = (j%2)*40;
+			cpVect offset;
+			offset.x = (i*80) - 320 + stagger;
+			offset.y = (j*70) - 240;
+			//BUG: crazy fucking dmd codegen bug when optimizing (2.050)
+			foo = i;
+			
+			shape = cpSpaceAddShape(space, cpPolyShapeNew(staticBody, 3, tris.ptr, offset));
+			shape.e = 1.0f; shape.u = 1.0f;
+			shape.layers = NOT_GRABABLE_MASK;
+		}
+	}
+	
+	// Add lots of pentagons.
+	for(int i=0; i<300; i++){
+		_body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForPoly(1.0f, NUM_VERTS, verts.ptr, cpvzero)));
+		cpFloat x = frand()*640 - 320;
+		_body.p = cpv(x, 350);
+		
+		shape = cpSpaceAddShape(space, cpPolyShapeNew(_body, NUM_VERTS, verts.ptr, cpvzero));
+		shape.e = 0.0f; shape.u = 0.4f;
+	}
+	
+	return space;
+}
+
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo Plink = {
+	"Plink",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/Pump.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,173 @@
+
+// written in the D programming language
+
+module samples.Pump;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+import gameApp;
+
+import std.math;
+
+enum M_PI = PI;
+enum M_PI_2 = PI*0.5f;
+
+static cpSpace *space;
+static cpConstraint *motor;
+
+enum numBalls = 5;
+static cpBody *balls[numBalls];
+
+static void
+update(int ticks)
+{
+	cpFloat coef = (2.0f + arrowDirection.y)/3.0f;
+	cpFloat rate = arrowDirection.x*30.0f*coef;
+	
+	cpSimpleMotorSetRate(motor, rate);
+	motor.maxForce = (rate ? 1000000.0f : 0.0f);
+
+	enum int steps = 2;
+	enum cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	for(int i=0; i<steps; i++){
+		cpSpaceStep(space, dt);
+		
+		for(int j=0; j<numBalls; j++){
+			cpBody *ball = balls[j];
+			if(ball.p.x > 320.0f){
+				ball.v = cpvzero;
+				ball.p = cpv(-224.0f, 200.0f);
+			}
+		}
+	}
+}
+
+static cpBody *
+add_ball(cpVect pos)
+{
+	cpBody *_body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForCircle(1.0f, 30, 0, cpvzero)));
+	_body.p = pos;
+	
+	cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(_body, 30, cpvzero));
+	shape.e = 0.0f; shape.u = 0.5f;
+	
+	return _body;
+}
+
+static cpSpace *
+init()
+{
+	space = cpSpaceNew();
+	space.gravity = cpv(0, -600);
+	
+	cpBody *staticBody = &space.staticBody;
+	cpShape *shape;
+	
+	// beveling all of the line segments slightly helps prevent things from getting stuck on cracks
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-256,16), cpv(-256,300), 2.0f));
+	shape.e = 0.0f; shape.u = 0.5f; shape.layers = 1;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-256,16), cpv(-192,0), 2.0f));
+	shape.e = 0.0f; shape.u = 0.5f; shape.layers = 1;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-192,0), cpv(-192, -64), 2.0f));
+	shape.e = 0.0f; shape.u = 0.5f; shape.layers = 1;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-128,-64), cpv(-128,144), 2.0f));
+	shape.e = 0.0f; shape.u = 0.5f; shape.layers = 1;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-192,80), cpv(-192,176), 2.0f));
+	shape.e = 0.0f; shape.u = 0.5f; shape.layers = 1;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-192,176), cpv(-128,240), 2.0f));
+	shape.e = 0.0f; shape.u = 0.0f; shape.layers = 1;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-128,144), cpv(192,64), 2.0f));
+	shape.e = 0.0f; shape.u = 0.5f; shape.layers = 1;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	cpVect verts[] = [
+		cpv(-30,-80),
+		cpv(-30, 80),
+		cpv( 30, 64),
+		cpv( 30,-80),
+	];
+
+	cpBody *plunger = cpSpaceAddBody(space, cpBodyNew(1.0f, INFINITY));
+	plunger.p = cpv(-160,-80);
+	
+	shape = cpSpaceAddShape(space, cpPolyShapeNew(plunger, 4, verts.ptr, cpvzero));
+	shape.e = 1.0f; shape.u = 0.5f; shape.layers = 1;
+	
+	// add balls to hopper
+	for(int i=0; i<numBalls; i++)
+		balls[i] = add_ball(cpv(-224 + i,80 + 64*i));
+	
+	// add small gear
+	cpBody *smallGear = cpSpaceAddBody(space, cpBodyNew(10.0f, cpMomentForCircle(10.0f, 80, 0, cpvzero)));
+	smallGear.p = cpv(-160,-160);
+	cpBodySetAngle(smallGear, cast(float)-M_PI_2);
+
+	shape = cpSpaceAddShape(space, cpCircleShapeNew(smallGear, 80.0f, cpvzero));
+	shape.layers = 0;
+	
+	cpSpaceAddConstraint(space, cpPivotJointNew2(staticBody, smallGear, cpv(-160,-160), cpvzero));
+
+	// add big gear
+	cpBody *bigGear = cpSpaceAddBody(space, cpBodyNew(40.0f, cpMomentForCircle(40.0f, 160, 0, cpvzero)));
+	bigGear.p = cpv(80,-160);
+	cpBodySetAngle(bigGear, cast(float)M_PI_2);
+	
+	shape = cpSpaceAddShape(space, cpCircleShapeNew(bigGear, 160.0f, cpvzero));
+	shape.layers = 0;
+	
+	cpSpaceAddConstraint(space, cpPivotJointNew2(staticBody, bigGear, cpv(80,-160), cpvzero));
+
+	// connect the plunger to the small gear.
+	cpSpaceAddConstraint(space, cpPinJointNew(smallGear, plunger, cpv(80,0), cpv(0,0)));
+	// connect the gears.
+	cpSpaceAddConstraint(space, cpGearJointNew(smallGear, bigGear, -M_PI_2, -2.0f));
+	
+	
+	// feeder mechanism
+	cpFloat bottom = -300.0f;
+	cpFloat top = 32.0f;
+	cpBody *feeder = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForSegment(1.0f, cpv(-224.0f, bottom), cpv(-224.0f, top))));
+	feeder.p = cpv(-224, (bottom + top)/2.0f);
+	
+	cpFloat len = top - bottom;
+	cpSpaceAddShape(space, cpSegmentShapeNew(feeder, cpv(0.0f, len/2.0f), cpv(0.0f, -len/2.0f), 20.0f));
+	
+	cpSpaceAddConstraint(space, cpPivotJointNew2(staticBody, feeder, cpv(-224.0f, bottom), cpv(0.0f, -len/2.0f)));
+	cpVect anchr = cpBodyWorld2Local(feeder, cpv(-224.0f, -160.0f));
+	cpSpaceAddConstraint(space, cpPinJointNew(feeder, smallGear, anchr, cpv(0.0f, 80.0f)));
+
+	// motorize the second gear
+	motor = cpSpaceAddConstraint(space, cpSimpleMotorNew(staticBody, bigGear, 3.0f));
+
+	return space;
+}
+
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo Pump = {
+	"Pump",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/PyramidStack.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,87 @@
+
+// written in the D programming language
+
+module samples.PyramidStack;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+static cpSpace *space;
+
+static void
+update(int ticks)
+{
+	int steps = 3;
+	cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	for(int i=0; i<steps; i++){
+		cpSpaceStep(space, dt);
+	}
+}
+
+static cpSpace *
+init()
+{
+	cpResetShapeIdCounter();
+	
+	space = cpSpaceNew();
+	space.iterations = 30;
+	cpSpaceResizeStaticHash(space, 40.0f, 1000);
+	cpSpaceResizeActiveHash(space, 40.0f, 1000);
+	space.gravity = cpv(0, -100);
+	space.sleepTimeThreshold = 0.5f;
+	
+	cpBody *_body;
+	cpBody *staticBody = &space.staticBody;
+	cpShape *shape;
+	
+	// Create segments around the edge of the screen.
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	// Add lots of boxes.
+	for(int i=0; i<14; i++){
+		for(int j=0; j<=i; j++){
+			_body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForBox(1.0f, 30.0f, 30.0f)));
+			_body.p = cpv(j*32 - i*16, 300 - i*32);
+			
+			shape = cpSpaceAddShape(space, cpBoxShapeNew(_body, 30.0f, 30.0f));
+			shape.e = 0.0f; shape.u = 0.8f;
+		}
+	}
+	
+	// Add a ball to make things more interesting
+	cpFloat radius = 15.0f;
+	_body = cpSpaceAddBody(space, cpBodyNew(10.0f, cpMomentForCircle(10.0f, 0.0f, radius, cpvzero)));
+	_body.p = cpv(0, -240 + radius+5);
+
+	shape = cpSpaceAddShape(space, cpCircleShapeNew(_body, radius, cpvzero));
+	shape.e = 0.0f; shape.u = 0.9f;
+	
+	return space;
+}
+
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo PyramidStack = {
+	"Pyramid Stack",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/PyramidTopple.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,120 @@
+
+// written in the D programming language
+
+module samples.PyramidTopple;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+import std.math:PI;
+
+static cpSpace *space;
+
+static void
+update(int ticks)
+{
+	int steps = 3;
+	cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	for(int i=0; i<steps; i++)
+		cpSpaceStep(space, dt);
+}
+
+static cpSpace *
+init()
+{
+	cpResetShapeIdCounter();
+	
+	space = cpSpaceNew();
+	space.iterations = 30;
+	cpSpaceResizeActiveHash(space, 30.0f, 2999);
+	cpSpaceResizeStaticHash(space, 30.0f, 999);
+	space.gravity = cpv(0, -300);
+	space.sleepTimeThreshold = 0.5f;
+	
+	cpBody *_body;
+	
+	cpShape *shape;
+	
+	// Vertexes for the dominos.
+	int num = 4;
+	cpVect verts[] = [
+		cpv(-3,-20),
+		cpv(-3, 20),
+		cpv( 3, 20),
+		cpv( 3,-20),
+	];
+	
+	// Add a floor.
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(&space.staticBody, cpv(-600,-240), cpv(600,-240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	// Shared friction constant.
+	cpFloat u = 0.6f;
+	
+	// Add the dominoes. Skim over this. It doesn't do anything fancy, and it's hard to follow.
+	int n = 9;
+	for(int i=1; i<=n; i++){
+		cpVect offset = cpv(-i*60/2.0f, (n - i)*52);
+		
+		for(int j=0; j<i; j++){
+			_body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForPoly(1.0f, num, verts.ptr, cpvzero)));
+			_body.p = cpvadd(cpv(j*60, -220), offset);
+			
+			shape = cpSpaceAddShape(space, cpPolyShapeNew(_body, num, verts.ptr, cpvzero));
+			shape.e = 0.0f; shape.u = u;
+			
+
+			_body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForPoly(1.0f, num, verts.ptr, cpvzero)));
+			_body.p = cpvadd(cpv(j*60, -197), offset);
+			cpBodySetAngle(_body, cast(float)PI/2.0f);
+			
+			shape = cpSpaceAddShape(space, cpPolyShapeNew(_body, num, verts.ptr, cpvzero));
+			shape.e = 0.0f; shape.u = u;
+			
+			
+			if(j == (i - 1)) continue;
+			_body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForPoly(1.0f, num, verts.ptr, cpvzero)));
+			_body.p = cpvadd(cpv(j*60 + 30, -191), offset);
+			cpBodySetAngle(_body, cast(float)PI/2.0f);
+			
+			shape = cpSpaceAddShape(space, cpPolyShapeNew(_body, num, verts.ptr, cpvzero));
+			shape.e = 0.0f; shape.u = u;
+		}
+
+		_body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForPoly(1.0f, num, verts.ptr, cpvzero)));
+		_body.p = cpvadd(cpv(-17, -174), offset);
+		
+		shape = cpSpaceAddShape(space, cpPolyShapeNew(_body, num, verts.ptr, cpvzero));
+		shape.e = 0.0f; shape.u = u;
+		
+
+		_body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForPoly(1.0f, num, verts.ptr, cpvzero)));
+		_body.p = cpvadd(cpv((i - 1)*60 + 17, -174), offset);
+		
+		shape = cpSpaceAddShape(space, cpPolyShapeNew(_body, num, verts.ptr, cpvzero));
+		shape.e = 0.0f; shape.u = u;
+	}
+	
+	// Give the last domino a little tap.
+//	body.w = -1;
+//	body.v = cpv(-body.w*20, 0);
+	return space;
+}
+
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo PyramidTopple = {
+	"Pyramid Topple",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/Query.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,136 @@
+
+// written in the D programming language
+
+module samples.Query;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+import gameApp;
+
+import std.math;
+
+//extern cpVect			mousePos;
+
+static cpSpace *space;
+
+static cpShape *querySeg = null;
+
+
+static void
+update(int ticks)
+{
+	//messageString[0] = '\0';
+	
+	cpVect start = cpvzero;
+	cpVect end = /*cpv(0, 85);//*/mousePos;
+	cpVect lineEnd = end;
+	
+	//{
+	//	char infoString[1024];
+	//	sprintf(infoString, "Query: Dist(%f) Point%s, ", cpvdist(start, end), cpvstr(end));
+	//	strcat(messageString, infoString);
+	//}
+	
+	cpSegmentQueryInfo info = {};
+	if(cpSpaceSegmentQueryFirst(space, start, end, CP_ALL_LAYERS, CP_NO_GROUP, &info)){
+		cpVect point = cpSegmentQueryHitPoint(start, end, info);
+		lineEnd = cpvadd(point, cpvzero);//cpvmult(info.n, 4.0f));
+		
+		//char infoString[1024];
+		//sprintf(infoString, "Segment Query: Dist(%f) Normal%s", cpSegmentQueryHitDist(start, end, info), cpvstr(info.n));
+		//strcat(messageString, infoString);
+	} else {
+		//strcat(messageString, "Segment Query (None)");
+	}
+	
+	cpSegmentShapeSetEndpoints(querySeg, start, lineEnd);
+	cpShapeCacheBB(querySeg); // force it to update it's collision detection data so it will draw
+	
+	// normal other stuff.
+	int steps = 1;
+	cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	for(int i=0; i<steps; i++){
+		cpSpaceStep(space, dt);
+	}
+}
+
+static cpSpace *
+init()
+{
+	cpResetShapeIdCounter();
+	
+	space = cpSpaceNew();
+	space.elasticIterations = 0;
+	space.iterations = 5;
+
+	cpSpaceResizeStaticHash(space, 40.0f, 999);
+	cpSpaceResizeActiveHash(space, 30.0f, 2999);
+
+	cpBody *staticBody = &space.staticBody;
+	cpShape *shape;
+	
+	// add a non-collidable segment as a quick and dirty way to draw the query line
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpvzero, cpv(100.0f, 0.0f), 4.0f));
+	shape.layers = 0;
+	querySeg = shape;
+	
+	{ // add a fat segment
+		cpFloat mass = 1.0f;
+		cpFloat length = 100.0f;
+		cpVect a = cpv(-length/2.0f, 0.0f), b = cpv(length/2.0f, 0.0f);
+		
+		cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForSegment(mass, a, b)));
+		_body.p = cpv(0.0f, 100.0f);
+		
+		cpSpaceAddShape(space, cpSegmentShapeNew(_body, a, b, 20.0f));
+	}
+	
+	{ // add a static segment
+		cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(0, 300), cpv(300, 0), 0.0f));
+	}
+	
+	{ // add a pentagon
+		cpFloat mass = 1.0f;
+		const int NUM_VERTS = 5;
+		
+		cpVect verts[NUM_VERTS];
+		for(int i=0; i<NUM_VERTS; i++){
+			cpFloat angle = -2*PI*i/(cast(cpFloat) NUM_VERTS);
+			verts[i] = cpv(30*cos(angle), 30*sin(angle));
+		}
+		
+		cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, NUM_VERTS, verts.ptr, cpvzero)));
+		_body.p = cpv(50.0f, 50.0f);
+		
+		cpSpaceAddShape(space, cpPolyShapeNew(_body, NUM_VERTS, verts.ptr, cpvzero));
+	}
+	
+	{ // add a circle
+		cpFloat mass = 1.0f;
+		cpFloat r = 20.0f;
+		
+		cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, r, cpvzero)));
+		_body.p = cpv(100.0f, 100.0f);
+		
+		cpSpaceAddShape(space, cpCircleShapeNew(_body, r, cpvzero));
+	}
+	
+	return space;
+}
+
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo Query = {
+	"Segment Query",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/Sensors.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,142 @@
+
+// written in the D programming language
+
+module samples.Sensors;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+static cpSpace *space;
+
+enum CollisionTypes {
+	BALL_TYPE,
+	BLOCKING_SENSOR_TYPE,
+	CATCH_SENSOR_TYPE,
+};
+
+struct Emitter {
+	int queue;
+	int blocked;
+	cpVect position;
+}
+
+static Emitter emitterInstance;
+
+static cpBool
+blockerBegin(cpArbiter *arb, cpSpace *space, void *unused)
+{
+	mixin(CP_ARBITER_GET_SHAPES!("arb", "a", "b"));
+	Emitter *emitter = cast(Emitter *) a.data;
+	
+	emitter.blocked++;
+	
+	return cpFalse; // Return values from sensors callbacks are ignored,
+}
+
+static void
+blockerSeparate(cpArbiter *arb, cpSpace *space, void *unused)
+{
+	mixin(CP_ARBITER_GET_SHAPES!("arb", "a", "b"));
+	Emitter *emitter = cast(Emitter *)a.data;
+	
+	emitter.blocked--;
+}
+
+static void
+postStepRemove(cpSpace *space, cpShape *shape, void *unused)
+{
+	cpSpaceRemoveBody(space, shape._body);
+	cpSpaceRemoveShape(space, shape);
+	
+	cpBodyFree(shape._body);
+	cpShapeFree(shape);
+}
+
+static cpBool
+catcherBarBegin(cpArbiter *arb, cpSpace *space, void *unused)
+{
+	mixin(CP_ARBITER_GET_SHAPES!("arb", "a", "b"));
+	Emitter *emitter = cast(Emitter *) a.data;
+	
+	emitter.queue++;
+	cpSpaceAddPostStepCallback(space, cast(cpPostStepFunc)&postStepRemove, b, null);
+	
+	return cpFalse;
+}
+
+static cpFloat frand_unit(){return 2.0f*(frand()) - 1.0f;}
+
+static void
+update(int ticks)
+{
+	int steps = 1;
+	cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	if(!emitterInstance.blocked && emitterInstance.queue){
+		emitterInstance.queue--;
+		
+		cpBody *_body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForCircle(1.0f, 15.0f, 0.0f, cpvzero)));
+		_body.p = emitterInstance.position;
+		_body.v = cpvmult(cpv(frand_unit(), frand_unit()), 100.0f);
+		
+		cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(_body, 15.0f, cpvzero));
+		shape.collision_type = CollisionTypes.BALL_TYPE;
+	}
+	
+	for(int i=0; i<steps; i++){
+		cpSpaceStep(space, dt);
+	}
+}
+
+static cpSpace *
+init()
+{
+	cpResetShapeIdCounter();
+	
+	space = cpSpaceNew();
+	space.iterations = 10;
+	space.gravity = cpv(0, -100);
+	
+	cpBody *staticBody = &space.staticBody;
+	cpShape *shape;
+	
+	// Data structure for our ball emitter
+	// We'll use two sensors for it, one to see if the emitter is blocked
+	// a second to catch the balls and add them back to the emitter
+	emitterInstance.queue = 5;
+	emitterInstance.blocked = 0;
+	emitterInstance.position = cpv(0, 150);
+	
+	// Create our blocking sensor, so we know when the emitter is clear to emit another ball
+	shape = cpSpaceAddShape(space, cpCircleShapeNew(staticBody, 15.0f, emitterInstance.position));
+	shape.sensor = 1;
+	shape.collision_type = CollisionTypes.BLOCKING_SENSOR_TYPE;
+	shape.data = &emitterInstance;
+	
+	// Create our catch sensor to requeue the balls when they reach the bottom of the screen
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-2000, -200), cpv(2000, -200), 15.0f));
+	shape.sensor = 1;
+	shape.collision_type = CollisionTypes.CATCH_SENSOR_TYPE;
+	shape.data = &emitterInstance;
+	
+	cpSpaceAddCollisionHandler(space, CollisionTypes.BLOCKING_SENSOR_TYPE, CollisionTypes.BALL_TYPE, &blockerBegin, null, null, &blockerSeparate, null);
+	cpSpaceAddCollisionHandler(space, CollisionTypes.CATCH_SENSOR_TYPE, CollisionTypes.BALL_TYPE, &catcherBarBegin, null, null, null, null);
+	
+	return space;
+}
+
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo Sensors = {
+	"Sensors",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/Simple.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,89 @@
+
+// written in the D programming language
+
+module samples.Simple;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+static cpSpace *space;
+
+// Init is called by the demo code to set up the demo.
+static cpSpace *
+init()
+{
+	// Create a space, a space is a simulation world. It simulates the motions of rigid bodies,
+	// handles collisions between them, and simulates the joints between them.
+	space = cpSpaceNew();
+	
+	// Lets set some parameters of the space:
+	// More iterations make the simulation more accurate but slower
+	space.iterations = 10;
+	// These parameters tune the efficiency of the collision detection.
+	// For more info: http://code.google.com/p/chipmunk-physics/wiki/cpSpace
+	cpSpaceResizeStaticHash(space, 30.0f, 1000);
+	cpSpaceResizeActiveHash(space, 30.0f, 1000);
+	// Give it some gravity
+	space.gravity = cpv(0, -100);
+	
+	// Create A ground segment along the bottom of the screen
+	// By attaching it to &space.staticBody instead of a body, we make it a static shape.
+	cpShape *ground = cpSegmentShapeNew(&space.staticBody, cpv(-320,-240), cpv(320,-240), 0.0f);
+	// Set some parameters of the shape.
+	// For more info: http://code.google.com/p/chipmunk-physics/wiki/cpShape
+	ground.e = 1.0f; ground.u = 1.0f;
+	ground.layers = NOT_GRABABLE_MASK; // Used by the Demo mouse grabbing code
+	// Add the shape to the space as a static shape
+	// If a shape never changes position, add it as static so Chipmunk knows it only needs to
+	// calculate collision information for it once when it is added.
+	// Do not change the postion of a static shape after adding it.
+	cpSpaceAddShape(space, ground);
+	
+	// Add a moving circle object.
+	cpFloat radius = 15.0f;
+	cpFloat mass = 10.0f;
+	// This time we need to give a mass and moment of inertia when creating the circle.
+	cpBody *ballBody = cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero));
+	// Set some parameters of the body:
+	// For more info: http://code.google.com/p/chipmunk-physics/wiki/cpBody
+	ballBody.p = cpv(0, -100 + radius+50);
+	ballBody.v = cpv(0, -20);
+	// Add the body to the space so it will be simulated and move around.
+	cpSpaceAddBody(space, ballBody);
+	
+	
+	// Add a circle shape for the ball.
+	// Shapes are always defined relative to the center of gravity of the body they are attached to.
+	// When the body moves or rotates, the shape will move with it.
+	// Additionally, all of the cpSpaceAdd*() functions return the thing they added so you can create and add in one go.
+	cpShape *ballShape = cpSpaceAddShape(space, cpCircleShapeNew(ballBody, radius, cpvzero));
+	ballShape.e = 0.0f; ballShape.u = 0.9f;
+	
+	return space;
+}
+
+// Update is called by the demo code each frame.
+static void
+update(int ticks)
+{
+	// Chipmunk allows you to use a different timestep each frame, but it works much better when you use a fixed timestep.
+	// An excellent article on why fixed timesteps for game logic can be found here: http://gafferongames.com/game-physics/fix-your-timestep/
+	cpSpaceStep(space, 1.0f/60.0f);
+}
+
+// destroy is called by the demo code to free all the memory we've allocated
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo Simple = {
+	"Simple",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/Springies.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,151 @@
+
+// written in the D programming language
+
+module samples.Springies;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+static cpSpace *space;
+
+static cpFloat
+springForce(cpConstraint *spring, cpFloat dist)
+{
+	cpFloat clamp = 20.0f;
+	return cpfclamp(cpDampedSpringGetRestLength(spring) - dist, -clamp, clamp)*cpDampedSpringGetStiffness(spring);
+}
+
+static cpConstraint *
+new_spring(cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2, cpFloat restLength, cpFloat stiff, cpFloat damp)
+{
+	cpConstraint *spring = cpDampedSpringNew(a, b, anchr1, anchr2, restLength, stiff, damp);
+	cpDampedSpringSetSpringForceFunc(spring, &springForce);
+	
+	return spring;
+}
+
+static void
+update(int ticks)
+{
+	enum int steps = 1;
+	enum cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	for(int i=0; i<steps; i++){
+		cpSpaceStep(space, dt);
+	}
+}
+
+static cpBody *
+add_bar(cpVect a, cpVect b, int group)
+{
+	cpVect center = cpvmult(cpvadd(a, b), 1.0f/2.0f);
+	cpFloat length = cpvlength(cpvsub(b, a));
+	cpFloat mass = length/160.0f;
+	
+	cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, mass*length*length/12.0f));
+	_body.p = center;
+	
+	cpShape *shape = cpSpaceAddShape(space, cpSegmentShapeNew(_body, cpvsub(a, center), cpvsub(b, center), 10.0f));
+	shape.group = group;
+	
+	return _body;
+}
+
+static cpSpace *
+init()
+{
+	space = cpSpaceNew();
+	cpBody *staticBody = &space.staticBody;
+	
+	cpBody *body1  = add_bar(cpv(-240,  160), cpv(-160,   80), 1);
+	cpBody *body2  = add_bar(cpv(-160,   80), cpv( -80,  160), 1);
+	cpBody *body3  = add_bar(cpv(   0,  160), cpv(  80,    0), 0);
+	cpBody *body4  = add_bar(cpv( 160,  160), cpv( 240,  160), 0);
+	cpBody *body5  = add_bar(cpv(-240,    0), cpv(-160,  -80), 2);
+	cpBody *body6  = add_bar(cpv(-160,  -80), cpv( -80,    0), 2);
+	cpBody *body7  = add_bar(cpv( -80,    0), cpv(   0,    0), 2);
+	cpBody *body8  = add_bar(cpv(   0,  -80), cpv(  80,  -80), 0);
+	cpBody *body9  = add_bar(cpv( 240,   80), cpv( 160,    0), 3);
+	cpBody *body10 = add_bar(cpv( 160,    0), cpv( 240,  -80), 3);
+	cpBody *body11 = add_bar(cpv(-240,  -80), cpv(-160, -160), 4);
+	cpBody *body12 = add_bar(cpv(-160, -160), cpv( -80, -160), 0);
+	cpBody *body13 = add_bar(cpv(   0, -160), cpv(  80, -160), 0);
+	cpBody *body14 = add_bar(cpv( 160, -160), cpv( 240, -160), 0);
+	
+	cpSpaceAddConstraint(space, cpPivotJointNew2( body1,  body2, cpv( 40,-40), cpv(-40,-40)));
+	cpSpaceAddConstraint(space, cpPivotJointNew2( body5,  body6, cpv( 40,-40), cpv(-40,-40)));
+	cpSpaceAddConstraint(space, cpPivotJointNew2( body6,  body7, cpv( 40, 40), cpv(-40,  0)));
+	cpSpaceAddConstraint(space, cpPivotJointNew2( body9, body10, cpv(-40,-40), cpv(-40, 40)));
+	cpSpaceAddConstraint(space, cpPivotJointNew2(body11, body12, cpv( 40,-40), cpv(-40,  0)));
+	
+	cpFloat stiff = 100.0f;
+	cpFloat damp = 0.5f;
+	cpSpaceAddConstraint(space, new_spring(staticBody,  body1, cpv(-320,  240), cpv(-40, 40), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring(staticBody,  body1, cpv(-320,   80), cpv(-40, 40), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring(staticBody,  body1, cpv(-160,  240), cpv(-40, 40), 0.0f, stiff, damp));
+
+	cpSpaceAddConstraint(space, new_spring(staticBody,  body2, cpv(-160,  240), cpv( 40, 40), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring(staticBody,  body2, cpv(   0,  240), cpv( 40, 40), 0.0f, stiff, damp));
+
+	cpSpaceAddConstraint(space, new_spring(staticBody,  body3, cpv(  80,  240), cpv(-40, 80), 0.0f, stiff, damp));
+
+	cpSpaceAddConstraint(space, new_spring(staticBody,  body4, cpv(  80,  240), cpv(-40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring(staticBody,  body4, cpv( 320,  240), cpv( 40,  0), 0.0f, stiff, damp));
+
+	cpSpaceAddConstraint(space, new_spring(staticBody,  body5, cpv(-320,   80), cpv(-40, 40), 0.0f, stiff, damp));
+	
+	cpSpaceAddConstraint(space, new_spring(staticBody,  body9, cpv( 320,  80), cpv( 40, 40), 0.0f, stiff, damp));
+
+	cpSpaceAddConstraint(space, new_spring(staticBody, body10, cpv( 320,   0), cpv( 40,-40), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring(staticBody, body10, cpv( 320,-160), cpv( 40,-40), 0.0f, stiff, damp));
+
+	cpSpaceAddConstraint(space, new_spring(staticBody, body11, cpv(-320,-160), cpv(-40, 40), 0.0f, stiff, damp));
+
+	cpSpaceAddConstraint(space, new_spring(staticBody, body12, cpv(-240,-240), cpv(-40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring(staticBody, body12, cpv(   0,-240), cpv( 40,  0), 0.0f, stiff, damp));
+
+	cpSpaceAddConstraint(space, new_spring(staticBody, body13, cpv(   0,-240), cpv(-40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring(staticBody, body13, cpv(  80,-240), cpv( 40,  0), 0.0f, stiff, damp));
+
+	cpSpaceAddConstraint(space, new_spring(staticBody, body14, cpv(  80,-240), cpv(-40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring(staticBody, body14, cpv( 240,-240), cpv( 40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring(staticBody, body14, cpv( 320,-160), cpv( 40,  0), 0.0f, stiff, damp));
+
+	cpSpaceAddConstraint(space, new_spring( body1,  body5, cpv( 40,-40), cpv(-40, 40), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body1,  body6, cpv( 40,-40), cpv( 40, 40), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body2,  body3, cpv( 40, 40), cpv(-40, 80), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body3,  body4, cpv(-40, 80), cpv(-40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body3,  body4, cpv( 40,-80), cpv(-40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body3,  body7, cpv( 40,-80), cpv( 40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body3,  body7, cpv(-40, 80), cpv(-40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body3,  body8, cpv( 40,-80), cpv( 40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body3,  body9, cpv( 40,-80), cpv(-40,-40), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body4,  body9, cpv( 40,  0), cpv( 40, 40), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body5, body11, cpv(-40, 40), cpv(-40, 40), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body5, body11, cpv( 40,-40), cpv( 40,-40), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body7,  body8, cpv( 40,  0), cpv(-40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body8, body12, cpv(-40,  0), cpv( 40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body8, body13, cpv(-40,  0), cpv(-40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body8, body13, cpv( 40,  0), cpv( 40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring( body8, body14, cpv( 40,  0), cpv(-40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring(body10, body14, cpv( 40,-40), cpv(-40,  0), 0.0f, stiff, damp));
+	cpSpaceAddConstraint(space, new_spring(body10, body14, cpv( 40,-40), cpv(-40,  0), 0.0f, stiff, damp));
+	
+	return space;
+}
+
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo Springies = {
+	"Springies",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/Tank.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,134 @@
+
+// written in the D programming language
+
+module samples.Tank;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+import gameApp;
+
+static cpSpace *space;
+
+static cpBody *tankBody;
+static cpBody *tankControlBody;
+
+static void
+update(int ticks)
+{
+	enum int steps = 1;
+	enum cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	for(int i=0; i<steps; i++){
+		// turn the control _body based on the angle relative to the actual _body
+		cpVect mouseDelta = cpvsub(mousePos, tankBody.p);
+		cpFloat turn = cpvtoangle(cpvunrotate(tankBody.rot, mouseDelta));
+		cpBodySetAngle(tankControlBody, tankBody.a - turn);
+		
+		// drive the tank towards the mouse
+		if(cpvnear(mousePos, tankBody.p, 30.0)){
+			tankControlBody.v = cpvzero; // stop
+		} else {
+			cpFloat direction = (cpvdot(mouseDelta, tankBody.rot) > 0.0 ? 1.0 : -1.0);
+			tankControlBody.v = cpvrotate(tankBody.rot, cpv(30.0f*direction, 0.0f));
+		}
+		
+		cpSpaceStep(space, dt);
+	}
+}
+
+static cpBody *
+add_box(cpFloat size, cpFloat mass)
+{
+	cpVect verts[] = [
+		cpv(-size,-size),
+		cpv(-size, size),
+		cpv( size, size),
+		cpv( size,-size),
+	];
+	
+	cpFloat radius = cpvlength(cpv(size, size));
+
+	cpBody *_body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 4, verts.ptr, cpvzero)));
+	_body.p = cpv(frand()*(640 - 2*radius) - (320 - radius), frand()*(480 - 2*radius) - (240 - radius));
+	
+	cpShape *shape = cpSpaceAddShape(space, cpPolyShapeNew(_body, 4, verts.ptr, cpvzero));
+	shape.e = 0.0f; shape.u = 0.7f;
+	
+	return _body;
+}
+
+static cpSpace *
+init()
+{
+	cpResetShapeIdCounter();
+	
+	space = cpSpaceNew();
+	cpSpaceResizeActiveHash(space, 30.0f, 1000);
+	space.iterations = 10;
+	space.sleepTimeThreshold = 0.5f;
+	
+	cpBody *staticBody = &space.staticBody;
+	cpShape *shape;
+		
+	// Create segments around the edge of the screen.
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,240), cpv(320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	for(int i=0; i<50; i++){
+		cpBody *_body = add_box(10.0, 1.0);
+		
+		cpConstraint *pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(staticBody, _body, cpvzero, cpvzero));
+		pivot.biasCoef = 0.0f; // disable joint correction
+		pivot.maxForce = 1000.0f; // emulate linear friction
+		
+		cpConstraint *gear = cpSpaceAddConstraint(space, cpGearJointNew(staticBody, _body, 0.0f, 1.0f));
+		gear.biasCoef = 0.0f; // disable joint correction
+		gear.maxForce = 5000.0f; // emulate angular friction
+	}
+	
+	// We joint the tank to the control _body and control the tank indirectly by modifying the control _body.
+	tankControlBody = cpBodyNew(INFINITY, INFINITY);
+	tankBody = add_box(15.0, 10.0);
+	
+	cpConstraint *pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(tankControlBody, tankBody, cpvzero, cpvzero));
+	pivot.biasCoef = 0.0f; // disable joint correction
+	pivot.maxForce = 10000.0f; // emulate linear friction
+	
+	cpConstraint *gear = cpSpaceAddConstraint(space, cpGearJointNew(tankControlBody, tankBody, 0.0f, 1.0f));
+	gear.biasCoef = 1.0f; // limit angular correction rate
+	gear.maxBias = 1.0f; // limit angular correction rate
+	gear.maxForce = 500000.0f; // emulate angular friction
+		
+	return space;
+}
+
+static void
+destroy()
+{
+	cpBodyFree(tankControlBody);
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo Tank = {
+	"Tank",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/TheoJansen.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,161 @@
+
+// written in the D programming language
+
+module samples.TheoJansen;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+import gameApp;
+
+import std.math;
+
+enum M_PI = PI;
+enum M_PI_2 = PI*0.5f;
+
+static cpSpace *space;
+
+static cpConstraint *motor;
+
+static void
+update(int ticks)
+{
+	cpFloat coef = (2.0f + arrowDirection.y)/3.0f;
+	cpFloat rate = arrowDirection.x*10.0f*coef;
+	cpSimpleMotorSetRate(motor, rate);
+	motor.maxForce = (rate) ? 100000.0f : 0.0f;
+	
+	enum int steps = 3;
+	enum cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	for(int i=0; i<steps; i++){
+		cpSpaceStep(space, dt);
+	}
+}
+
+static cpFloat seg_radius = 3.0f;
+
+static void
+make_leg(cpFloat side, cpFloat offset, cpBody *chassis, cpBody *crank, cpVect anchor)
+{
+	cpVect a, b;
+	cpShape *shape;
+	
+	cpFloat leg_mass = 1.0f;
+
+	// make leg
+	a = cpvzero, b = cpv(0.0f, side);
+	cpBody *upper_leg = cpBodyNew(leg_mass, cpMomentForSegment(leg_mass, a, b));
+	upper_leg.p = cpv(offset, 0.0f);
+	cpSpaceAddBody(space, upper_leg);
+	cpSpaceAddShape(space, cpSegmentShapeNew(upper_leg, a, b, seg_radius));
+	cpSpaceAddConstraint(space, cpPivotJointNew2(chassis, upper_leg, cpv(offset, 0.0f), cpvzero));
+	
+	// lower leg
+	a = cpvzero, b = cpv(0.0f, -1.0f*side);
+	cpBody *lower_leg = cpBodyNew(leg_mass, cpMomentForSegment(leg_mass, a, b));
+	lower_leg.p = cpv(offset, -side);
+	cpSpaceAddBody(space, lower_leg);
+	shape = cpSegmentShapeNew(lower_leg, a, b, seg_radius);
+	shape.group = 1;
+	cpSpaceAddShape(space, shape);
+	shape = cpCircleShapeNew(lower_leg, seg_radius*2.0f, b);
+	shape.group = 1;
+	shape.e = 0.0f; shape.u = 1.0f;
+	cpSpaceAddShape(space, shape);
+	cpSpaceAddConstraint(space, cpPinJointNew(chassis, lower_leg, cpv(offset, 0.0f), cpvzero));
+	
+	cpSpaceAddConstraint(space, cpGearJointNew(upper_leg, lower_leg, 0.0f, 1.0f));
+	
+	cpConstraint *constraint;
+	cpFloat diag = cpfsqrt(side*side + offset*offset);
+	
+	constraint = cpPinJointNew(crank, upper_leg, anchor, cpv(0.0f, side));
+	cpPinJointSetDist(constraint, diag);
+	cpSpaceAddConstraint(space, constraint);
+	constraint = cpPinJointNew(crank, lower_leg, anchor, cpvzero);
+	cpPinJointSetDist(constraint, diag);
+	cpSpaceAddConstraint(space, constraint);
+}
+
+static cpSpace *
+init()
+{
+	space = cpSpaceNew();
+	
+	cpResetShapeIdCounter();
+	
+	space = cpSpaceNew();
+	space.iterations = 20;
+	space.gravity = cpv(0,-500);
+	
+	cpBody *staticBody = &space.staticBody;
+	cpShape *shape;
+	cpVect a, b;
+	
+	// Create segments around the edge of the screen.
+	shape = cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f);
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	cpSpaceAddShape(space, shape);
+
+	shape = cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f);
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	cpSpaceAddShape(space, shape);
+
+	shape = cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f);
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	cpSpaceAddShape(space, shape);
+	
+	cpFloat offset = 30.0f;
+
+	// make chassis
+	cpFloat chassis_mass = 2.0f;
+	a = cpv(-offset, 0.0f), b = cpv(offset, 0.0f);
+	cpBody *chassis = cpBodyNew(chassis_mass, cpMomentForSegment(chassis_mass, a, b));
+	cpSpaceAddBody(space, chassis);
+	shape = cpSegmentShapeNew(chassis, a, b, seg_radius);
+	shape.group = 1;
+	cpSpaceAddShape(space, shape);
+	
+	// make crank
+	cpFloat crank_mass = 1.0f;
+	cpFloat crank_radius = 13.0f;
+	cpBody *crank = cpBodyNew(crank_mass, cpMomentForCircle(crank_mass, crank_radius, 0.0f, cpvzero));
+	cpSpaceAddBody(space, crank);
+	shape = cpCircleShapeNew(crank, crank_radius, cpvzero);
+	shape.group = 1;
+	cpSpaceAddShape(space, shape);
+	cpSpaceAddConstraint(space, cpPivotJointNew2(chassis, crank, cpvzero, cpvzero));
+	
+	cpFloat side = 30.0f;
+	
+	int num_legs = 2;
+	for(int i=0; i<num_legs; i++){
+		make_leg(side,  offset, chassis, crank, cpvmult(cpvforangle(cast(cpFloat)(2*i+0)/cast(cpFloat)num_legs*M_PI), crank_radius));
+		make_leg(side, -offset, chassis, crank, cpvmult(cpvforangle(cast(cpFloat)(2*i+1)/cast(cpFloat)num_legs*M_PI), crank_radius));
+	}
+	
+	motor = cpSimpleMotorNew(chassis, crank, 6.0f);
+	cpSpaceAddConstraint(space, motor);
+
+	return space;
+}
+
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo TheoJansen = {
+	"Theo Jansen Machine",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/Tumble.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,117 @@
+
+// written in the D programming language
+
+module samples.Tumble;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+static cpSpace *space;
+static cpBody *staticBody;
+
+static void
+update(int ticks)
+{
+	enum int steps = 3;
+	enum cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	for(int i=0; i<steps; i++){
+		cpSpaceStep(space, dt);
+		
+		// Manually update the position of the static shape so that
+		// the box rotates.
+		cpBodyUpdatePosition(staticBody, dt);
+		
+		// Because the box was added as a static shape and we moved it
+		// we need to manually rehash the static spatial hash.
+		cpSpaceRehashStatic(space);
+	}
+}
+
+static cpSpace *
+init()
+{
+	staticBody = cpBodyNew(INFINITY, INFINITY);
+	
+	cpResetShapeIdCounter();
+	
+	space = cpSpaceNew();
+	cpSpaceResizeActiveHash(space, 40.0f, 999);
+	cpSpaceResizeStaticHash(space, 40.0f, 99);
+	space.gravity = cpv(0, -600);
+	
+	cpBody *_body;
+	cpShape *shape;
+	
+	// Vertexes for the bricks
+	int num = 4;
+	cpVect verts[] = [
+		cpv(-30,-15),
+		cpv(-30, 15),
+		cpv( 30, 15),
+		cpv( 30,-15),
+	];
+	
+	// Set up the static box.
+	cpVect a = cpv(-200, -200);
+	cpVect b = cpv(-200,  200);
+	cpVect c = cpv( 200,  200);
+	cpVect d = cpv( 200, -200);
+	
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, a, b, 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, b, c, 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, c, d, 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, d, a, 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	// Give the box a little spin.
+	// Because staticBody is never added to the space, we will need to
+	// update it ourselves. (see above).
+	// NOTE: Normally you would want to add the segments as normal and not static shapes.
+	// I'm just doing it to demonstrate the cpSpaceRehashStatic() function.
+	staticBody.w = 0.4f;
+	
+	int foo;
+	// Add the bricks.
+	for(int i=0; i<3; i++){
+		for(int j=0; j<7; j++){
+			_body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForPoly(1.0f, num, verts.ptr, cpvzero)));
+			_body.p = cpv(i*60 - 150, j*30 - 150);
+	
+			//BUG: crazy fucking dmd codegen bug when optimizing (2.050)
+			foo = i+j;
+			
+			shape = cpSpaceAddShape(space, cpPolyShapeNew(_body, num, verts.ptr, cpvzero));
+			shape.e = 0.0f; shape.u = 0.7f;
+		}
+	}
+	
+	return space;
+}
+
+static void
+destroy()
+{
+	cpBodyFree(staticBody);
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo Tumble = {
+	"Tumble",
+	null,
+	&init,
+	&update,
+	&destroy,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/samples/UnsafeOps.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,98 @@
+
+// written in the D programming language
+
+module samples.UnsafeOps;
+
+import chipmunkd.chipmunk;
+
+import samples.ChipmunkDemo;
+
+import gameApp;
+
+import std.math;
+
+static cpSpace *space;
+
+enum M_PI = PI;
+enum M_PI_2 = PI*0.5f;
+
+enum NUM_CIRCLES = 30;
+
+static cpShape *circles[NUM_CIRCLES];
+static cpFloat circleRadius = 30.0f;
+
+static void
+update(int ticks)
+{
+	if(arrowDirection.y){
+		circleRadius = cpfmax(10.0f, circleRadius + arrowDirection.y);
+		
+		for(int i=0; i<NUM_CIRCLES; i++){
+			circles[i]._body.m = cpMomentForCircle(1.0f, 0.0f, circleRadius, cpvzero);
+			cpCircleShapeSetRadius(circles[i], circleRadius);
+		}
+	}
+	
+	int steps = 1;
+	cpFloat dt = 1.0f/60.0f/cast(cpFloat)steps;
+	
+	for(int i=0; i<steps; i++){
+		cpSpaceStep(space, dt);
+	}
+}
+
+static cpSpace *
+init()
+{
+	cpResetShapeIdCounter();
+	
+	space = cpSpaceNew();
+	space.iterations = 5;
+	space.gravity = cpv(0, -100);
+	
+	cpSpaceResizeStaticHash(space, 40.0f, 999);
+	cpSpaceResizeActiveHash(space, 30.0f, 2999);
+	
+	cpBody *_body, staticBody = &space.staticBody;
+	cpShape *shape;
+	
+	shape = cpSpaceAddStaticShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddStaticShape(space, cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+
+	shape = cpSpaceAddStaticShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f));
+	shape.e = 1.0f; shape.u = 1.0f;
+	shape.layers = NOT_GRABABLE_MASK;
+	
+	for(int i=0; i<NUM_CIRCLES; i++){
+		_body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForCircle(1.0f, 0.0f, circleRadius, cpvzero)));
+		_body.p = cpvmult(cpv(frand()*2.0f - 1.0f, frand()*2.0f - 1.0f), circleRadius*5.0f);
+		
+		circles[i] = shape = cpSpaceAddShape(space, cpCircleShapeNew(_body, circleRadius, cpvzero));
+		shape.e = 0.0f; shape.u = 1.0f;
+	}
+	
+	//strcat(messageString,
+	//	"chipmunk_unsafe.h Contains functions for changing shapes, but they can cause severe stability problems if used incorrectly.\n"
+	//	"Shape changes occur as instantaneous changes to position without an accompanying velocity change. USE WITH CAUTION!");
+	return space;
+}
+
+static void
+destroy()
+{
+	cpSpaceFreeChildren(space);
+	cpSpaceFree(space);
+}
+
+chipmunkDemo UnsafeOps = {
+	"Unsafe Operations",
+	null,
+	&init,
+	&update,
+	&destroy,
+};