diff --git a/Chipmunk2D-7.0.2/include/chipmunk/chipmunk.h b/Chipmunk2D-7.0.2/include/chipmunk/chipmunk.h
--- a/Chipmunk2D-7.0.2/include/chipmunk/chipmunk.h
+++ b/Chipmunk2D-7.0.2/include/chipmunk/chipmunk.h
@@ -1,15 +1,15 @@
 /* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
- * 
+ *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * in the Software without restriction, including without limitation the rights
  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  * copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in
  * all copies or substantial portions of the Software.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -58,7 +58,7 @@
 #define cpAssertHard(__condition__, ...) if(!(__condition__)){cpMessage(#__condition__, __FILE__, __LINE__, 1, 1, __VA_ARGS__); abort();}
 
 #include "chipmunk_types.h"
-	
+
 /// @defgroup misc Misc
 /// @{
 
@@ -115,11 +115,12 @@
 #include "cpTransform.h"
 #include "cpSpatialIndex.h"
 
-#include "cpArbiter.h"	
+#include "cpArbiter.h"
 
 #include "cpBody.h"
 #include "cpShape.h"
 #include "cpPolyShape.h"
+#include "cpPolyline.h"
 
 #include "cpConstraint.h"
 
@@ -191,7 +192,7 @@
 #if __has_extension(blocks)
 // Define alternate block based alternatives for a few of the callback heavy functions.
 // Collision handlers are post-step callbacks are not included to avoid memory management issues.
-// If you want to use blocks for those and are aware of how to correctly manage the memory, the implementation is trivial. 
+// If you want to use blocks for those and are aware of how to correctly manage the memory, the implementation is trivial.
 
 void cpSpaceEachBody_b(cpSpace *space, void (^block)(cpBody *body));
 void cpSpaceEachShape_b(cpSpace *space, void (^block)(cpShape *shape));
diff --git a/Chipmunk2D-7.0.2/include/chipmunk/cpPolyline.h b/Chipmunk2D-7.0.2/include/chipmunk/cpPolyline.h
--- a/Chipmunk2D-7.0.2/include/chipmunk/cpPolyline.h
+++ b/Chipmunk2D-7.0.2/include/chipmunk/cpPolyline.h
@@ -1,3 +1,6 @@
+#ifndef __POLYLINE_H__
+#define __POLYLINE_H__
+
 // Copyright 2013 Howling Moon Software. All rights reserved.
 // See http://chipmunk2d.net/legal.php for more information.
 
@@ -6,7 +9,7 @@
 // cpPolyline structs are intended to be passed by value and destroyed when you are done with them.
 typedef struct cpPolyline {
   int count, capacity;
-  cpVect verts[];
+  cpVect verts[0];
 } cpPolyline;
 
 /// Destroy and free a polyline instance.
@@ -68,3 +71,5 @@
 CP_EXPORT cpPolylineSet *cpPolylineConvexDecomposition(cpPolyline *line, cpFloat tol);
 
 #define cpPolylineConvexDecomposition_BETA cpPolylineConvexDecomposition
+
+#endif // __POLYLINE_H__
diff --git a/Chipmunk2D-7.0.2/src/cpPolyline.c b/Chipmunk2D-7.0.2/src/cpPolyline.c
--- a/Chipmunk2D-7.0.2/src/cpPolyline.c
+++ b/Chipmunk2D-7.0.2/src/cpPolyline.c
@@ -26,11 +26,11 @@
 cpPolylineMake(int capacity)
 {
 	capacity = (capacity > DEFAULT_POLYLINE_CAPACITY ? capacity : DEFAULT_POLYLINE_CAPACITY);
-	
+
 	cpPolyline *line = (cpPolyline *)cpcalloc(1, cpPolylineSizeForCapacity(capacity));
 	line->count = 0;
 	line->capacity = capacity;
-	
+
 	return line;
 }
 
@@ -41,7 +41,7 @@
 	line->count = 2;
 	line->verts[0] = a;
 	line->verts[1] = b;
-	
+
 	return line;
 }
 
@@ -63,15 +63,15 @@
 cpPolylineGrow(cpPolyline *line, int count)
 {
   line->count += count;
-  
+
   int capacity = line->capacity;
   while(line->count > capacity) capacity *= 2;
-  
+
   if(line->capacity < capacity){
     line->capacity = capacity;
 		line = (cpPolyline*) cprealloc(line, cpPolylineSizeForCapacity(capacity));
   }
-	
+
 	return line;
 }
 
@@ -82,7 +82,7 @@
   int count = line->count;
   line = cpPolylineGrow(line, 1);
   line->verts[count] = v;
-	
+
 	return line;
 }
 
@@ -96,7 +96,7 @@
   line = cpPolylineGrow(line, 1);
   memmove(line->verts + 1, line->verts, count*sizeof(cpVect));
   line->verts[0] = v;
-	
+
 	return line;
 }
 
@@ -117,7 +117,7 @@
 		length += cpvdist(points[i], points[Next(i, count)]);
 		if(length > min) return cpFalse;
 	}
-  
+
   return cpTrue;
 }
 
@@ -136,20 +136,20 @@
 cpPolylineSimplifyVertexes(cpPolyline *line, cpFloat tol)
 {
 	cpPolyline *reduced = cpPolylineMake2(0, line->verts[0], line->verts[1]);
-	
+
 	cpFloat minSharp = -cpfcos(tol);
-	
+
 	for(int i=2; i<line->count; i++){
 		cpVect vert = line->verts[i];
 		cpFloat sharp = Sharpness(reduced->verts[reduced->count - 2], reduced->verts[reduced->count - 1], vert);
-		
+
 		if(sharp <= minSharp){
 			reduced->verts[reduced->count - 1] = vert;
 		} else {
 			reduced = cpPolylinePush(reduced, vert);
 		}
 	}
-	
+
 	if(
 		cpPolylineIsClosed(line) &&
 		Sharpness(reduced->verts[reduced->count - 2], reduced->verts[0], reduced->verts[1]) < minSharp
@@ -157,7 +157,7 @@
 		reduced->verts[0] = reduced->verts[reduced->count - 2];
 		reduced->count--;
 	}
-	
+
 	// TODO shrink
 	return reduced;
 }
@@ -171,35 +171,35 @@
 ){
 	// Early exit if the points are adjacent
   if((end - start + length)%length < 2) return reduced;
-  
+
 	cpVect a = verts[start];
 	cpVect b = verts[end];
-	
+
 	// Check if the length is below the threshold
 	if(cpvnear(a, b, min) && cpPolylineIsShort(verts, length, start, end, min)) return reduced;
-	
+
 	// Find the maximal vertex to split and recurse on
 	cpFloat max = 0.0;
 	int maxi = start;
-	
+
 	cpVect n = cpvnormalize(cpvperp(cpvsub(b, a)));
 	cpFloat d = cpvdot(n, a);
-	
+
 	for(int i=Next(start, length); i!=end; i=Next(i, length)){
 		cpFloat dist = fabs(cpvdot(n, verts[i]) - d);
-		
+
 		if(dist > max){
 			max = dist;
 			maxi = i;
 		}
 	}
-	
+
 	if(max > tol){
     reduced = DouglasPeucker(verts, reduced, length, start, maxi, min, tol);
 		reduced = cpPolylinePush(reduced, verts[maxi]);
     reduced = DouglasPeucker(verts, reduced, length, maxi, end, min, tol);
 	}
-	
+
 	return reduced;
 }
 
@@ -210,13 +210,13 @@
 cpPolylineSimplifyCurves(cpPolyline *line, cpFloat tol)
 {
 	cpPolyline *reduced = cpPolylineMake(line->count);
-	
+
 	cpFloat min = tol/2.0f;
-  
+
   if(cpPolylineIsClosed(line)){
 		int start, end;
     cpLoopIndexes(line->verts, line->count - 1, &start, &end);
-    
+
 		reduced = cpPolylinePush(reduced, line->verts[start]);
 		reduced = DouglasPeucker(line->verts, reduced, line->count - 1, start, end, min, tol);
 		reduced = cpPolylinePush(reduced, line->verts[end]);
@@ -227,7 +227,7 @@
 		reduced = DouglasPeucker(line->verts, reduced, line->count, 0, line->count - 1, min, tol);
 		reduced = cpPolylinePush(reduced, line->verts[line->count - 1]);
   }
-	
+
 	return cpPolylineShrink(reduced);
 }
 
@@ -245,7 +245,7 @@
 	set->count = 0;
 	set->capacity = 8;
 	set->lines = (cpPolyline**) cpcalloc(set->capacity, sizeof(cpPolyline));
-	
+
   return set;
 }
 
@@ -264,7 +264,7 @@
 			cpPolylineFree(set->lines[i]);
 		}
 	}
-	
+
 	cpfree(set->lines);
 }
 
@@ -283,12 +283,12 @@
 cpPolylineSetFindEnds(cpPolylineSet *set, cpVect v){
 	int count = set->count;
 	cpPolyline **lines = set->lines;
-	
+
   for(int i=0; i<count; i++){
 		cpPolyline *line = lines[i];
     if(cpveql(line->verts[line->count - 1], v)) return i;
   }
-  
+
   return -1;
 }
 
@@ -297,11 +297,11 @@
 cpPolylineSetFindStarts(cpPolylineSet *set, cpVect v){
 	int count = set->count;
 	cpPolyline **lines = set->lines;
-	
+
   for(int i=0; i<count; i++){
     if(cpveql(lines[i]->verts[0], v)) return i;
   }
-  
+
   return -1;
 }
 
@@ -315,7 +315,7 @@
     set->capacity *= 2;
     set->lines = (cpPolyline**) cprealloc(set->lines, set->capacity*sizeof(cpPolyline));
   }
-  
+
 	set->lines[set->count - 1] = line;
 }
 
@@ -332,13 +332,13 @@
 {
   cpPolyline *lbefore = set->lines[before];
   cpPolyline *lafter = set->lines[after];
-  
+
   // append
   int count = lbefore->count;
   lbefore = cpPolylineGrow(lbefore, lafter->count);
   memmove(lbefore->verts + count, lafter->verts, lafter->count*sizeof(cpVect));
 	set->lines[before] = lbefore;
-  
+
   // delete lafter
   set->count--;
 	cpPolylineFree(set->lines[after]);
@@ -352,7 +352,7 @@
 {
   int before = cpPolylineSetFindEnds(lines, v0);
   int after = cpPolylineSetFindStarts(lines, v1);
-  
+
   if(before >= 0 && after >= 0){
     if(before == after){
       // loop by pushing v1 onto before
@@ -381,7 +381,7 @@
 	cpPolyline *hull = cpPolylineMake(line->count + 1);
 	hull->count = cpConvexHull(line->count, line->verts, hull->verts, NULL, tol);
 	hull = cpPolylinePush(hull, hull->verts[0]);
-	
+
 	return cpPolylineShrink(hull);
 }
 
@@ -399,26 +399,26 @@
 {
 	cpFloat min = INFINITY;
 	cpFloat feature = -1.0;
-	
+
 	for(int i=1; i<count-1; i++){
 		int index = (notch.i + i)%count;
-		
+
 		cpVect seg_a = verts[index];
 		cpVect seg_b = verts[Next(index, count)];
-		
+
 		cpFloat thing_a = cpvcross(notch.n, cpvsub(seg_a, notch.v));
 		cpFloat thing_b = cpvcross(notch.n, cpvsub(seg_b, notch.v));
 		if(thing_a*thing_b <= 0.0){
 			cpFloat t = thing_a/(thing_a - thing_b);
 			cpFloat dist = cpvdot(notch.n, cpvsub(cpvlerp(seg_a, seg_b, t), notch.v));
-			
+
 			if(dist >= 0.0 && dist <= min){
 				min = dist;
 				feature = index + t;
 			}
 		}
 	}
-	
+
 	return feature;
 }
 
@@ -428,29 +428,29 @@
 //	cpVect a = verts[(notch.i + count - 1)%count];
 //	cpVect b = verts[(notch.i + 1)%count];
 //	cpVect n = cpvnormalize(cpvadd(cpvnormalize(cpvsub(notch.v, a)), cpvnormalize(cpvsub(notch.v, b))));
-//	
+//
 //	cpFloat min = INFINITY;
 //	cpFloat feature = -1.0;
-//	
+//
 //	for(int i=1; i<count-1; i++){
 //		int index = (notch.i + i)%count;
-//		
+//
 //		cpVect seg_a = verts[index];
 //		cpVect seg_b = verts[Next(index, count)];
-//		
+//
 //		cpFloat thing_a = cpvcross(n, cpvsub(seg_a, notch.v));
 //		cpFloat thing_b = cpvcross(n, cpvsub(seg_b, notch.v));
 //		if(thing_a*thing_b <= 0.0){
 //			cpFloat t = thing_a/(thing_a - thing_b);
 //			cpFloat dist = cpvdot(n, cpvsub(cpvlerp(seg_a, seg_b, t), notch.v));
-//			
+//
 //			if(dist >= 0.0 && dist <= min){
 //				min = dist;
 //				feature = index + t;
 //			}
 //		}
 //	}
-//	
+//
 //	cpAssertSoft(feature >= 0.0, "No closest features detected. This is likely due to a self intersecting polygon.");
 //	return feature;
 //}
@@ -476,35 +476,35 @@
 //{
 //	cpFloat min = INFINITY;
 //	cpFloat feature = -1.0;
-//	
+//
 //	cpVect support_a = verts[(notch.i - 1 + count)%count];
 //	cpVect support_b = verts[(notch.i + 1)%count];
-//	
+//
 //	cpVect clip_a = cpvlerp(support_a, support_b, 0.1);
 //	cpVect clip_b = cpvlerp(support_b, support_b, 0.9);
-//	
+//
 //	for(int i=1; i<count - 1; i++){
 //		int index = (notch.i + i)%count;
 //		cpVect seg_a = verts[index];
 //		cpVect seg_b = verts[Next(index, count)];
-//		
+//
 //		cpVect delta_a = cpvsub(seg_a, notch.v);
 //		cpVect delta_b = cpvsub(seg_b, notch.v);
-//		
+//
 //		// Ignore if the segment faces away from the point.
 //		if(cpvcross(delta_b, delta_a) > 0.0){
 //			struct Range range1 = clip_range(delta_a, delta_b, cpvsub(notch.v, clip_a));
 //			struct Range range2 = clip_range(delta_a, delta_b, cpvsub(clip_b, notch.v));
-//			
+//
 //			cpFloat min_t = cpfmax(0.0, cpfmax(range1.min, range2.min));
 //			cpFloat max_t = cpfmin(1.0, cpfmin(range1.max, range2.max));
-//			
+//
 //			// Ignore if the segment has been completely clipped away.
 //			if(min_t < max_t){
 //				cpVect seg_delta = cpvsub(seg_b, seg_a);
 //				cpFloat closest_t = cpfclamp(cpvdot(seg_delta, cpvsub(notch.v, seg_a))/cpvlengthsq(seg_delta), min_t, max_t);
 //				cpVect closest = cpvlerp(seg_a, seg_b, closest_t);
-//				
+//
 //				cpFloat dist = cpvdistsq(notch.v, closest);
 //				if(dist < min){
 //					min = dist;
@@ -513,7 +513,7 @@
 //			}
 //		}
 //	}
-//	
+//
 //	cpAssertWarn(feature >= 0.0, "Internal Error: No closest features detected.");
 //	return feature;
 //}
@@ -523,18 +523,18 @@
 //{
 //	cpVect v = verts[notch_i];
 //	cpVect n = cpvnormalize(cpvsub(verts[index], v));
-//	
+//
 //	for(int i=0; i<count; i++){
 //		if(i == index || i == Next(i, count) || i == notch_i || i == Next(notch_i, count)) continue;
-//		
+//
 //		cpVect seg_a = verts[i];
 //		cpVect seg_b = verts[Next(i, count)];
-//		
+//
 //		cpFloat thing_a = cpvcross(n, cpvsub(seg_a, v));
 //		cpFloat thing_b = cpvcross(n, cpvsub(seg_b, v));
 //		if(thing_a*thing_b <= 0.0) return cpTrue;
 //	}
-//	
+//
 //	return cpFalse;
 //}
 //
@@ -543,17 +543,17 @@
 //{
 //	cpFloat min = INFINITY;
 //	cpFloat feature = -1.0;
-//	
+//
 //	for(int i=Next(notch.b, count); i!=notch.a; i=Next(i, count)){
 //		cpVect v = verts[i];
 //		cpFloat weight = (1.0 + 0.1*convexity[i])/(1.0*cpvdist(notch.v, v));
-//		
+//
 //		if(weight <= min && VertexUnobscured(count, verts, i, notch.i)){
 //			min = weight;
 //			feature = i;
 //		}
 //	}
-//	
+//
 //	cpAssertSoft(feature >= 0.0, "No closest features detected. This is likely due to a self intersecting polygon.");
 //	return feature;
 //}
@@ -563,79 +563,82 @@
 {
 	struct Notch notch = {};
 	int j = Next(first, count);
-	
+
 	for(int i=0; i<hullCount; i++){
 		cpVect a = hullVerts[i];
 		cpVect b = hullVerts[Next(i, hullCount)];
-		
+
 		// TODO use a cross check instead?
 		cpVect n = cpvnormalize(cpvrperp(cpvsub(a, b)));
 		cpFloat d = cpvdot(n, a);
-		
+
 		cpVect v = verts[j];
 		while(!cpveql(v, b)){
 			cpFloat depth = cpvdot(n, v) - d;
-			
+
 			if(depth > notch.d){
 				notch.d = depth;
 				notch.i = j;
 				notch.v = v;
 				notch.n = n;
 			}
-			
+
 			j = Next(j, count);
 			v = verts[j];
 		}
-		
+
 		j = Next(j, count);
 	}
-	
+
 	return notch;
 }
 
 static inline int IMAX(int a, int b){return (a > b ? a : b);}
 
+#define MAX_RECURSION_DEPTH 100
+
 static void
-ApproximateConcaveDecomposition(cpVect *verts, int count, cpFloat tol, cpPolylineSet *set)
+ApproximateConcaveDecomposition(int depth, cpVect *verts, int count, cpFloat tol, cpPolylineSet *set)
 {
 	int first;
 	cpVect *hullVerts = (cpVect*) alloca(count*sizeof(cpVect));
 	int hullCount = cpConvexHull(count, verts, hullVerts, &first, 0.0);
-	
+
+
 	if(hullCount != count){
 		struct Notch notch = DeepestNotch(count, verts, hullCount, hullVerts, first, tol);
-		
-		if(notch.d > tol){
+
+		if(notch.d > tol && depth < MAX_RECURSION_DEPTH){
 			cpFloat steiner_it = FindSteiner(count, verts, notch);
-			
+
 			if(steiner_it >= 0.0){
 				int steiner_i = (int)steiner_it;
 				cpVect steiner = cpvlerp(verts[steiner_i], verts[Next(steiner_i, count)], steiner_it - steiner_i);
-				
+
 				// Vertex counts NOT including the steiner point.
 				int sub1_count = (steiner_i - notch.i + count)%count + 1;
 				int sub2_count = count - (steiner_i - notch.i + count)%count;
 				cpVect *scratch = (cpVect*) alloca((IMAX(sub1_count, sub2_count) + 1)*sizeof(cpVect));
-				
+
 				for(int i=0; i<sub1_count; i++) scratch[i] = verts[(notch.i + i)%count];
 				scratch[sub1_count] = steiner;
-				ApproximateConcaveDecomposition(scratch, sub1_count + 1, tol, set);
-				
+				ApproximateConcaveDecomposition(depth+1,scratch, sub1_count + 1, tol, set);
+
 				for(int i=0; i<sub2_count; i++) scratch[i] = verts[(steiner_i + 1 + i)%count];
 				scratch[sub2_count] = steiner;
-				ApproximateConcaveDecomposition(scratch, sub2_count + 1, tol, set);
-				
+				ApproximateConcaveDecomposition(depth+1,scratch, sub2_count + 1, tol, set);
+
 				return;
 			}
 		}
 	}
-	
+
 	cpPolyline *hull = cpPolylineMake(hullCount + 1);
-	
+
 	memcpy(hull->verts, hullVerts, hullCount*sizeof(cpVect));
 	hull->verts[hullCount] = hullVerts[0];
 	hull->count = hullCount + 1;
-	
+
 	cpPolylineSetPush(set, hull);
 }
 
@@ -644,9 +647,9 @@
 {
 	cpAssertSoft(cpPolylineIsClosed(line), "Cannot decompose an open polygon.");
 	cpAssertSoft(cpAreaForPoly(line->count, line->verts, 0.0) >= 0.0, "Winding is backwards. (Are you passing a hole?)");
-	
+
 	cpPolylineSet *set = cpPolylineSetNew();
-	ApproximateConcaveDecomposition(line->verts, line->count - 1, tol, set);
-	
+	ApproximateConcaveDecomposition(0, line->verts, line->count - 1, tol, set);
+
 	return set;
 }
diff --git a/chiphunk.cabal b/chiphunk.cabal
--- a/chiphunk.cabal
+++ b/chiphunk.cabal
@@ -4,16 +4,16 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: e6e34b0f3a7347782c482b79a689d989bed10c837045db9756529b0b9ba1bc80
+-- hash: 2ad0ea8a28a41b5969685566dd3f472c558141b895393055c85b73f670c63421
 
 name:           chiphunk
-version:        0.1.1.0
+version:        0.1.2.0
 synopsis:       Haskell bindings for Chipmunk2D physics engine
 description:    Please see the README on GitHub at <https://github.com/CthulhuDen/chiphunk#readme>
 category:       Physics
 homepage:       https://github.com/CthulhuDen/chiphunk#readme
 author:         Cthulhu
-maintainer:     cthulhu.den@gmail.com
+maintainer:     cthulhu.den@gmail.com, lemmih@gmail.com
 copyright:      Cthulhu (c) 2018
 license:        BSD3
 license-file:   LICENSE
@@ -72,6 +72,7 @@
   hs-source-dirs:
       src
   ghc-options: -Wall
+  cc-options: -DNDEBUG
   include-dirs:
       Chipmunk2D-7.0.2/include
       src/Chiphunk
diff --git a/src/Chiphunk/Low.hs b/src/Chiphunk/Low.hs
--- a/src/Chiphunk/Low.hs
+++ b/src/Chiphunk/Low.hs
@@ -419,6 +419,8 @@
   , shapeFriction
   , shapeSurfaceVelocity
   , shapeCollisionType
+  , shapeMass
+  , shapeDensity
   , ShapeFilter (..)
   , shapeFilter
   , shapeSpace
@@ -500,6 +502,7 @@
 
     -- *** Convex Hull Helper Functions
   , convexHull
+  , convexDecomposition
 
     -- ** Modifying 'Shape's
 
diff --git a/src/Chiphunk/Low/Helper.chs b/src/Chiphunk/Low/Helper.chs
--- a/src/Chiphunk/Low/Helper.chs
+++ b/src/Chiphunk/Low/Helper.chs
@@ -10,6 +10,7 @@
   , areaForPoly
   , centroidForPoly
   , convexHull
+  , convexDecomposition
   ) where
 
 import Data.VectorSpace
@@ -97,3 +98,16 @@
   alloca $ \pFst -> do
     n <- {# call cpConvexHull as c_convexHull #} (fromIntegral $ length vs) pVs pRes pFst (realToFrac tol)
     (,) <$> peekArray (fromIntegral n) pRes <*> (fromIntegral <$> peek pFst)
+
+convexDecomposition :: [Vect] -> Double -> [[Vect]]
+convexDecomposition [] _ = []
+convexDecomposition concavePolygon tol = unsafePerformIO $
+  withPolylinePtr (Polyline counterClockwise) $ \lineP -> do
+    setP <- {# call cpPolylineConvexDecomposition #} lineP (realToFrac tol)
+    set <- peekPolylineSet setP
+    {# call cpPolylineSetFree #} setP 1
+    return $ map unPolyline $ unPolylineSet set
+  where
+    counterClockwise
+      | areaForPoly concavePolygon 0 < 0 = reverse concavePolygon
+      | otherwise                        = concavePolygon
diff --git a/src/Chiphunk/Low/Shape.chs b/src/Chiphunk/Low/Shape.chs
--- a/src/Chiphunk/Low/Shape.chs
+++ b/src/Chiphunk/Low/Shape.chs
@@ -9,6 +9,8 @@
   , shapeFriction
   , shapeSurfaceVelocity
   , shapeCollisionType
+  , shapeMass
+  , shapeDensity
   , ShapeFilter (..)
   , ShapeFilterPtr
   , shapeFilter
@@ -107,6 +109,20 @@
 -- See the callbacks section for more information.
 shapeCollisionType :: Shape -> StateVar CollisionType
 shapeCollisionType = mkStateVar cpShapeGetCollisionType cpShapeSetCollisionType
+
+{# fun unsafe cpShapeGetMass {`Shape'} -> `Double' #}
+
+{# fun unsafe cpShapeSetMass {`Shape', `Double'} -> `()' #}
+
+shapeMass :: Shape -> StateVar Double
+shapeMass = mkStateVar cpShapeGetMass cpShapeSetMass
+
+{# fun unsafe cpShapeGetDensity {`Shape'} -> `Double' #}
+
+{# fun unsafe cpShapeSetDensity {`Shape', `Double'} -> `()' #}
+
+shapeDensity :: Shape -> StateVar Double
+shapeDensity = mkStateVar cpShapeGetDensity cpShapeSetDensity
 
 -- | Fast collision filtering type that is used to determine if two objects collide
 -- before calling collision or query callbacks.
diff --git a/src/Chiphunk/Low/Types.chs b/src/Chiphunk/Low/Types.chs
--- a/src/Chiphunk/Low/Types.chs
+++ b/src/Chiphunk/Low/Types.chs
@@ -21,6 +21,12 @@
   , CollisionType
   , CPBool
   , mkStateVar
+  , Polyline(..)
+  , PolylinePtr
+  , PolylineSet(..)
+  , PolylineSetPtr
+  , withPolylinePtr
+  , peekPolylineSet
   ) where
 
 import Data.Cross
@@ -244,3 +250,36 @@
 -- | 'makeStateVar' lifted to reader monad
 mkStateVar :: (a -> IO b) -> (a -> b -> IO ()) -> a -> StateVar b
 mkStateVar g s i = makeStateVar (g i) (s i)
+
+{# pointer *cpPolyline as PolylinePtr -> Polyline #}
+newtype Polyline = Polyline { unPolyline :: [Vect] }
+
+foreign import ccall w_cpPolylineVerts :: Ptr Polyline -> Ptr Vect
+
+withPolylinePtr :: Polyline -> (Ptr Polyline -> IO a) -> IO a
+withPolylinePtr (Polyline verts) fn = do
+    allocaBytes (sizeOf (undefined :: Vect) * (count+10)) $ \p -> do
+
+      {# set cpPolyline->count #} p $ fromIntegral count
+      {# set cpPolyline->capacity #} p $ fromIntegral count
+      let vp = plusPtr p {# offsetof cpPolyline->verts #}
+      pokeArray vp verts
+      fn p
+  where
+    count = length verts
+
+peekPolyline :: Ptr Polyline -> IO Polyline
+peekPolyline p = do
+  count <- fromIntegral <$> {# get cpPolyline->count #} p
+  let vp = w_cpPolylineVerts p
+  Polyline <$> peekArray count vp
+
+{# pointer *cpPolylineSet as PolylineSetPtr -> PolylineSet #}
+
+data PolylineSet = PolylineSet { unPolylineSet :: [Polyline] }
+
+peekPolylineSet :: Ptr PolylineSet -> IO PolylineSet
+peekPolylineSet p = do
+  count <- fromIntegral <$> {# get cpPolyline->count #} p
+  lp <- {# get cpPolylineSet->lines #} p
+  PolylineSet <$> (mapM peekPolyline =<< peekArray count lp)
diff --git a/src/Chiphunk/wrapper.c b/src/Chiphunk/wrapper.c
--- a/src/Chiphunk/wrapper.c
+++ b/src/Chiphunk/wrapper.c
@@ -174,3 +174,7 @@
 {
   *out = cpArbiterGetPointB(arbiter, i);
 }
+
+cpVect* w_cpPolylineVerts(cpPolyline* line) {
+  return line->verts;
+}
diff --git a/src/Chiphunk/wrapper.h b/src/Chiphunk/wrapper.h
--- a/src/Chiphunk/wrapper.h
+++ b/src/Chiphunk/wrapper.h
@@ -69,3 +69,5 @@
 void w_cpArbiterGetPointA(const cpArbiter *arbiter, int i, cpVect *out);
 
 void w_cpArbiterGetPointB(const cpArbiter *arbiter, int i, cpVect *out);
+
+cpVect* w_cpPolylineVerts(cpPolyline *line);
