diff trunk/chipmunkd/cpPolyShape.d @ 4:7ebbd4d05553

initial commit
author Extrawurst
date Thu, 02 Dec 2010 02:11:26 +0100
parents
children df4ebc8add66
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/chipmunkd/cpPolyShape.d	Thu Dec 02 02:11:26 2010 +0100
@@ -0,0 +1,315 @@
+
+// written in the D programming language
+
+module chipmunkd.cpPolyShape;
+
+import chipmunkd.cpShape;
+import chipmunkd.cpBody;
+import chipmunkd.chipmunk;
+import chipmunkd.chipmunk_types_h;
+import chipmunkd.cpVect_h;
+import chipmunkd.cpBB;
+
+// Axis structure used by cpPolyShape.
+struct cpPolyShapeAxis{
+	// normal
+	cpVect n;
+	// distance from origin
+	cpFloat d;
+}
+
+// Convex polygon shape structure.
+struct cpPolyShape{
+	cpShape shape;
+	
+	// Vertex and axis lists.
+	int numVerts;
+	cpVect *verts;
+	cpPolyShapeAxis *axes;
+
+	// Transformed vertex and axis lists.
+	cpVect *tVerts;
+	cpPolyShapeAxis *tAxes;
+}
+
+//// Basic allocation functions.
+//cpPolyShape *cpPolyShapeAlloc(void);
+//cpPolyShape *cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int numVerts, cpVect *verts, cpVect offset);
+//cpShape *cpPolyShapeNew(cpBody *body, int numVerts, cpVect *verts, cpVect offset);
+//
+//cpPolyShape *cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height);
+//cpShape *cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height);
+//
+//// Check that a set of vertexes has a correct winding and that they are convex
+//cpBool cpPolyValidate(cpVect *verts, int numVerts);
+//
+//int cpPolyShapeGetNumVerts(cpShape *shape);
+//cpVect cpPolyShapeGetVert(cpShape *shape, int idx);
+
+// *** inlined utility functions
+
+// Returns the minimum distance of the polygon to the axis.
+static cpFloat
+cpPolyShapeValueOnAxis(const cpPolyShape *poly, const cpVect n, const cpFloat d)
+{
+	const cpVect *verts = poly.tVerts;
+	cpFloat min = cpvdot(n, verts[0]);
+	
+	int i;
+	for(i=1; i<poly.numVerts; i++)
+		min = cpfmin(min, cpvdot(n, verts[i]));
+	
+	return min - d;
+}
+
+// Returns true if the polygon contains the vertex.
+static cpBool
+cpPolyShapeContainsVert(const cpPolyShape *poly, const cpVect v)
+{
+	const cpPolyShapeAxis *axes = poly.tAxes;
+	
+	int i;
+	for(i=0; i<poly.numVerts; i++){
+		cpFloat dist = cpvdot(axes[i].n, v) - axes[i].d;
+		if(dist > 0.0f) return cpFalse;
+	}
+	
+	return cpTrue;
+}
+
+// Same as cpPolyShapeContainsVert() but ignores faces pointing away from the normal.
+static cpBool
+cpPolyShapeContainsVertPartial(const cpPolyShape *poly, const cpVect v, const cpVect n)
+{
+	const cpPolyShapeAxis *axes = poly.tAxes;
+	
+	int i;
+	for(i=0; i<poly.numVerts; i++){
+		if(cpvdot(axes[i].n, n) < 0.0f) continue;
+		cpFloat dist = cpvdot(axes[i].n, v) - axes[i].d;
+		if(dist > 0.0f) return cpFalse;
+	}
+	
+	return cpTrue;
+}
+
+// cpPolyShape.c ---------------------------------
+
+
+cpPolyShape *
+cpPolyShapeAlloc()
+{
+	return cast(cpPolyShape *)cpcalloc(1, cpPolyShape.sizeof);
+}
+
+static void
+cpPolyShapeTransformVerts(cpPolyShape *poly, cpVect p, cpVect rot)
+{
+	cpVect *src = poly.verts;
+	cpVect *dst = poly.tVerts;
+	
+	for(int i=0; i<poly.numVerts; i++)
+		dst[i] = cpvadd(p, cpvrotate(src[i], rot));
+}
+
+static void
+cpPolyShapeTransformAxes(cpPolyShape *poly, cpVect p, cpVect rot)
+{
+	cpPolyShapeAxis *src = poly.axes;
+	cpPolyShapeAxis *dst = poly.tAxes;
+	
+	for(int i=0; i<poly.numVerts; i++){
+		cpVect n = cpvrotate(src[i].n, rot);
+		dst[i].n = n;
+		dst[i].d = cpvdot(p, n) + src[i].d;
+	}
+}
+
+static cpBB
+cpPolyShapeCacheData(cpShape *shape, cpVect p, cpVect rot)
+{
+	cpPolyShape *poly = cast(cpPolyShape *)shape;
+	
+	cpFloat l, b, r, t;
+	
+	cpPolyShapeTransformAxes(poly, p, rot);
+	cpPolyShapeTransformVerts(poly, p, rot);
+	
+	cpVect *verts = poly.tVerts;
+	l = r = verts[0].x;
+	b = t = verts[0].y;
+	
+	// TODO do as part of cpPolyShapeTransformVerts?
+	for(int i=1; i<poly.numVerts; i++){
+		cpVect v = verts[i];
+		
+		l = cpfmin(l, v.x);
+		r = cpfmax(r, v.x);
+		
+		b = cpfmin(b, v.y);
+		t = cpfmax(t, v.y);
+	}
+	
+	return cpBBNew(l, b, r, t);
+}
+
+static void
+cpPolyShapeDestroy(cpShape *shape)
+{
+	cpPolyShape *poly = cast(cpPolyShape *)shape;
+	
+	cpfree(poly.verts);
+	cpfree(poly.tVerts);
+	
+	cpfree(poly.axes);
+	cpfree(poly.tAxes);
+}
+
+static cpBool
+cpPolyShapePointQuery(cpShape *shape, cpVect p){
+	return cpBBcontainsVect(shape.bb, p) && cpPolyShapeContainsVert(cast(cpPolyShape *)shape, p);
+}
+
+static void
+cpPolyShapeSegmentQuery(cpShape *shape, cpVect a, cpVect b, cpSegmentQueryInfo *info)
+{
+	cpPolyShape *poly = cast(cpPolyShape *)shape;
+	cpPolyShapeAxis *axes = poly.tAxes;
+	cpVect *verts = poly.tVerts;
+	int numVerts = poly.numVerts;
+	
+	for(int i=0; i<numVerts; i++){
+		cpVect n = axes[i].n;
+		cpFloat an = cpvdot(a, n);
+		if(axes[i].d > an) continue;
+		
+		cpFloat bn = cpvdot(b, n);
+		cpFloat t = (axes[i].d - an)/(bn - an);
+		if(t < 0.0f || 1.0f < t) continue;
+		
+		cpVect point = cpvlerp(a, b, t);
+		cpFloat dt = -cpvcross(n, point);
+		cpFloat dtMin = -cpvcross(n, verts[i]);
+		cpFloat dtMax = -cpvcross(n, verts[(i+1)%numVerts]);
+		
+		if(dtMin <= dt && dt <= dtMax){
+			info.shape = shape;
+			info.t = t;
+			info.n = n;
+		}
+	}
+}
+
+static /+const+/ cpShapeClass polyClass = {
+	cpShapeType.CP_POLY_SHAPE,
+	&cpPolyShapeCacheData,
+	&cpPolyShapeDestroy,
+	&cpPolyShapePointQuery,
+	&cpPolyShapeSegmentQuery,
+};
+
+cpBool
+cpPolyValidate(cpVect *verts, int numVerts)
+{
+	for(int i=0; i<numVerts; i++){
+		cpVect a = verts[i];
+		cpVect b = verts[(i+1)%numVerts];
+		cpVect c = verts[(i+2)%numVerts];
+		
+		if(cpvcross(cpvsub(b, a), cpvsub(c, b)) > 0.0f)
+			return cpFalse;
+	}
+	
+	return cpTrue;
+}
+
+int
+cpPolyShapeGetNumVerts(cpShape *shape)
+{
+	assert(shape.klass == &polyClass, "Shape is not a poly shape.");
+	return (cast(cpPolyShape *)shape).numVerts;
+}
+
+cpVect
+cpPolyShapeGetVert(cpShape *shape, int idx)
+{
+	assert(shape.klass == &polyClass, "Shape is not a poly shape.");
+	assert(0 <= idx && idx < cpPolyShapeGetNumVerts(shape), "Index out of range.");
+	
+	return (cast(cpPolyShape *)shape).verts[idx];
+}
+
+
+static void
+setUpVerts(cpPolyShape *poly, int numVerts, cpVect *verts, cpVect offset)
+{
+	poly.numVerts = numVerts;
+
+	poly.verts	= cast(cpVect *)cpcalloc(numVerts, cpVect.sizeof);
+	poly.tVerts = cast(cpVect *)cpcalloc(numVerts, cpVect.sizeof);
+	poly.axes	= cast(cpPolyShapeAxis *)cpcalloc(numVerts, (cpPolyShapeAxis).sizeof);
+	poly.tAxes	= cast(cpPolyShapeAxis *)cpcalloc(numVerts, (cpPolyShapeAxis).sizeof);
+	
+	for(int i=0; i<numVerts; i++){
+		cpVect a = cpvadd(offset, verts[i]);
+		cpVect b = cpvadd(offset, verts[(i+1)%numVerts]);
+		cpVect n = cpvnormalize(cpvperp(cpvsub(b, a)));
+
+		poly.verts[i] = a;
+		poly.axes[i].n = n;
+		poly.axes[i].d = cpvdot(n, a);
+	}
+}
+
+cpPolyShape *
+cpPolyShapeInit(cpPolyShape *poly, cpBody *_body, int numVerts, cpVect *verts, cpVect offset)
+{
+	// Fail if the user attempts to pass a concave poly, or a bad winding.
+	assert(cpPolyValidate(verts, numVerts), "Polygon is concave or has a reversed winding.");
+	
+	setUpVerts(poly, numVerts, verts, offset);
+	cpShapeInit(cast(cpShape *)poly, &polyClass, _body);
+
+	return poly;
+}
+
+cpShape *
+cpPolyShapeNew(cpBody *_body, int numVerts, cpVect *verts, cpVect offset)
+{
+	return cast(cpShape *)cpPolyShapeInit(cpPolyShapeAlloc(), _body, numVerts, verts, offset);
+}
+
+cpPolyShape *
+cpBoxShapeInit(cpPolyShape *poly, cpBody *_body, cpFloat width, cpFloat height)
+{
+	cpFloat hw = width/2.0f;
+	cpFloat hh = height/2.0f;
+	
+	cpVect verts[] = [
+		cpv(-hw,-hh),
+		cpv(-hw, hh),
+		cpv( hw, hh),
+		cpv( hw,-hh),
+	];
+	
+	return cpPolyShapeInit(poly, _body, 4, verts.ptr, cpvzero);
+}
+
+cpShape *
+cpBoxShapeNew(cpBody *_body, cpFloat width, cpFloat height)
+{
+	return cast(cpShape *)cpBoxShapeInit(cpPolyShapeAlloc(), _body, width, height);
+}
+
+// Unsafe API (chipmunk_unsafe.h)
+
+void
+cpPolyShapeSetVerts(cpShape *shape, int numVerts, cpVect *verts, cpVect offset)
+{
+	assert(shape.klass == &polyClass, "Shape is not a poly shape.");
+	cpPolyShapeDestroy(shape);
+	setUpVerts(cast(cpPolyShape *)shape, numVerts, verts, offset);
+}
+
+
+