Prechádzať zdrojové kódy

edlp d8 corpus module starter operational with bower and gulp : y'a plus k'a

Bachir Soussi Chiadmi 6 rokov pred
rodič
commit
32b20d353c

+ 4 - 0
sites/all/modules/figli/edlp_corpus/.gitignore

@@ -0,0 +1,4 @@
+node_modules/
+.npm-debug-log
+package-lock.json
+bower_components/

+ 11963 - 0
sites/all/modules/figli/edlp_corpus/assets/dist/bower/physicsjs-full.js

@@ -0,0 +1,11963 @@
+/**
+ * PhysicsJS v0.7.0 - 2014-12-08
+ * A modular, extendable, and easy-to-use physics engine for javascript
+ * http://wellcaffeinated.net/PhysicsJS
+ *
+ * Copyright (c) 2014 Jasper Palfree <jasper@wellcaffeinated.net>
+ * Licensed MIT
+ */
+
+// ---
+// inside: src/intro.js
+
+(function (root, factory) {
+    if (typeof exports === 'object') {
+        // Node.
+        module.exports = factory.call(root);
+    } else if (typeof define === 'function' && define.amd) {
+        // AMD. Register as an anonymous module.
+        define(function(){ return factory.call(root) });
+    } else {
+        // Browser globals (root is window)
+        root.Physics = factory.call(root);
+    }
+}(typeof window !== 'undefined' ? window : this, function () {
+
+'use strict';
+
+var window = this;
+var document = window.document;
+
+/** related to: Physics.world
+ * Physics
+ *
+ * The top-level namespace. All of PhysicsJS is contained in
+ * the `Physics` namespace.
+ *
+ * It may (and should) be invoked as a function to create a world instance. For all intensive purposes, [[Physics]] and [[Physics.world]] are the same thing.
+ *
+ * See [[new Physics.world]] for config options and function signature.
+ *
+ * Example:
+ *
+ * ```javascript
+ * Physics( cfg, function( world ) {
+ *     // use world
+ * }); // -> world
+ * ```
+ **/
+var Physics = function Physics(){
+
+    return Physics.world.apply(Physics, arguments);
+};
+
+/**
+ * Physics.util
+ *
+ * Namespace for utility functions.
+ **/
+Physics.util = {};
+
+/**
+ * == Special ==
+ *
+ * This section contains miscellaneous functionality.
+ **/
+
+
+// ---
+// inside: src/math/aabb.js
+
+(function(){
+
+    /**
+     * Physics.aabb( minX, minY, maxX, maxY ) -> Object
+     * Physics.aabb( pt1, pt2 ) -> Object
+     * Physics.aabb( width, height[, pt] ) -> Object
+     * - minX (Number): The x coord of the "top left" point
+     * - minY (Number): The y coord of the "top left" point
+     * - maxX (Number): The x coord of the "bottom right" point
+     * - maxY (Number): The y coord of the "bottom right" point
+     * - pt1 (Vectorish): The first corner
+     * - pt2 (Vectorish): The opposite corner
+     * - width (Number): The width of the bounding box
+     * - height (Number): The height of the bounding box
+     * - pt (Vectorish): The center point of the bounding box
+     *
+     * Create an Axis Aligned Bounding Box.
+     *
+     * Signature:
+     *
+     * ```javascript
+     * {
+     *     x: Number, // the x coord of the center point
+     *     y: Number, // the y coord of the center point
+     *     hw: Number, // the half-width
+     *     hh: Number, // the half-height
+     * }
+     * ```
+     **/
+    Physics.aabb = function( minX, minY, maxX, maxY ){
+
+        var aabb = { x: 0, y: 0, hw: 0, hh: 0 };
+
+        if ( minX === undefined ){
+            return aabb;
+        }
+
+        if ( minX && minX.x !== undefined ){
+            // we have a point specified as first arg
+            maxX = minY.x;
+            maxY = minY.y;
+            minY = minX.y;
+            minX = minX.x;
+        }
+
+        if ( maxY === undefined && minX !== undefined && minY !== undefined ){
+
+            aabb.hw = minX * 0.5;
+            aabb.hh = minY * 0.5;
+
+            if ( maxX && maxX.x !== undefined ){
+                // we have a point specified as the third arg
+                // so we assume it's the center point
+                aabb.x = maxX.x;
+                aabb.y = maxX.y;
+            }
+
+            return aabb;
+        }
+
+        // here, we should have all the arguments as numbers
+        aabb.hw = Math.abs(maxX - minX) * 0.5;
+        aabb.hh = Math.abs(maxY - minY) * 0.5;
+        aabb.x = (maxX + minX) * 0.5;
+        aabb.y = (maxY + minY) * 0.5;
+
+        return aabb;
+    };
+
+    /**
+     * Physics.aabb.contains( aabb, pt ) -> Boolean
+     * - aabb (Object): The aabb
+     * - pt (Vectorish): The point
+     * + (Boolean): `true` if `pt` is inside `aabb`, `false` otherwise
+     *
+     * Check if a point is inside an aabb.
+     **/
+    Physics.aabb.contains = function contains( aabb, pt ){
+
+        return  (pt.x > (aabb.x - aabb.hw)) &&
+                (pt.x < (aabb.x + aabb.hw)) &&
+                (pt.y > (aabb.y - aabb.hh)) &&
+                (pt.y < (aabb.y + aabb.hh));
+    };
+
+    /**
+     * Physics.aabb.clone( aabb ) -> Object
+     * - aabb (Object): The aabb to clone
+     * + (Object): The clone
+     *
+     * Clone an aabb.
+     **/
+    Physics.aabb.clone = function( aabb ){
+        return {
+            x: aabb.x,
+            y: aabb.y,
+            hw: aabb.hw,
+            hh: aabb.hh
+        };
+    };
+
+    /**
+     * Physics.aabb.union( aabb1, aabb2[, modify] ) -> Object
+     * - aabb1 (Object): The first aabb (returned if modify is `true`)
+     * - aabb2 (Object): The second aabb
+     * + (Object): The union of two aabbs. If modify is `true`, then the first aabb will be modified and returned.
+     *
+     * Get the union of two aabbs.
+     **/
+    Physics.aabb.union = function( aabb1, aabb2, modify ){
+
+        var ret = modify === true ? aabb1 : {}
+            ,maxX = Math.max( aabb1.x + aabb1.hw, aabb2.x + aabb2.hw )
+            ,maxY = Math.max( aabb1.y + aabb1.hh, aabb2.y + aabb2.hh )
+            ,minX = Math.min( aabb1.x - aabb1.hw, aabb2.x - aabb2.hw )
+            ,minY = Math.min( aabb1.y - aabb1.hh, aabb2.y - aabb2.hh )
+            ;
+
+        ret.hw = Math.abs(maxX - minX) * 0.5;
+        ret.hh = Math.abs(maxY - minY) * 0.5;
+        ret.x = (maxX + minX) * 0.5;
+        ret.y = (maxY + minY) * 0.5;
+
+        return ret;
+    };
+
+
+    /**
+     * Physics.aabb.overlap( aabb1, aabb2 ) -> Boolean
+     * - aabb1 (Object): The first aabb
+     * - aabb2 (Object): The second aabb
+     * + (Boolean): `true` if they overlap, `false` otherwise
+     *
+     * Check if two AABBs overlap.
+     **/
+    Physics.aabb.overlap = function( aabb1, aabb2 ){
+
+        var min1 = aabb1.x - aabb1.hw
+            ,min2 = aabb2.x - aabb2.hw
+            ,max1 = aabb1.x + aabb1.hw
+            ,max2 = aabb2.x + aabb2.hw
+            ;
+
+        // first check x-axis
+
+        if ( (min2 <= max1 && max1 <= max2) || (min1 <= max2 && max2 <= max1) ){
+            // overlap in x-axis
+            // check y...
+            min1 = aabb1.y - aabb1.hh;
+            min2 = aabb2.y - aabb2.hh;
+            max1 = aabb1.y + aabb1.hh;
+            max2 = aabb2.y + aabb2.hh;
+
+            return (min2 <= max1 && max1 <= max2) || (min1 <= max2 && max2 <= max1);
+        }
+
+        // they don't overlap
+        return false;
+    };
+
+}());
+
+
+// ---
+// inside: src/math/gjk.js
+
+(function(){
+
+    // the algorithm doesn't always converge for curved shapes.
+    // need these constants to dictate how accurate we want to be.
+    var gjkAccuracy = 0.0001;
+    var gjkMaxIterations = 100;
+
+    // get the next search direction from two simplex points
+    var getNextSearchDir = function getNextSearchDir( ptA, ptB, dir ){
+
+        var ABdotB = ptB.normSq() - ptB.dot( ptA )
+            ,ABdotA = ptB.dot( ptA ) - ptA.normSq()
+            ;
+
+        // if the origin is farther than either of these points
+        // get the direction from one of those points to the origin
+        if ( ABdotB < 0 ){
+
+            return dir.clone( ptB ).negate();
+
+        } else if ( ABdotA > 0 ){
+
+            return dir.clone( ptA ).negate();
+
+        // otherwise, use the perpendicular direction from the simplex
+        } else {
+
+            // dir = AB = B - A
+            dir.clone( ptB ).vsub( ptA );
+            // if (left handed coordinate system)
+            // A cross AB < 0 then get perpendicular counterclockwise
+            return dir.perp( (ptA.cross( dir ) > 0) );
+        }
+    };
+
+    /** hide
+     * getClosestPoints( simplex ) -> Object
+     * - simplex (Array): The simplex
+     *
+     * Figure out the closest points on the original objects
+     * from the last two entries of the simplex
+     **/
+    var getClosestPoints = function getClosestPoints( simplex ){
+
+        // see http://www.codezealot.org/archives/153
+        // for algorithm details
+
+        // we know that the position of the last point
+        // is very close to the previous. (by nature of the distance test)
+        // this won't give great results for the closest
+        // points algorithm, so let's use the previous two
+        var len = simplex.length
+            ,last = simplex[ len - 2 ]
+            ,prev = simplex[ len - 3 ]
+            ,scratch = Physics.scratchpad()
+            ,A = scratch.vector().clone( last.pt )
+            // L = B - A
+            ,L = scratch.vector().clone( prev.pt ).vsub( A )
+            ,lambdaB
+            ,lambdaA
+            ;
+
+        if ( L.equals(Physics.vector.zero) ){
+
+            // oh.. it's a zero vector. So A and B are both the closest.
+            // just use one of them
+            return scratch.done({
+
+                a: last.a,
+                b: last.b
+            });
+        }
+
+        lambdaB = - L.dot( A ) / L.normSq();
+        lambdaA = 1 - lambdaB;
+
+        if ( lambdaA <= 0 ){
+            // woops.. that means the closest simplex point
+            // isn't on the line it's point B itself
+            return scratch.done({
+                a: prev.a,
+                b: prev.b
+            });
+        } else if ( lambdaB <= 0 ){
+            // vice versa
+            return scratch.done({
+                a: last.a,
+                b: last.b
+            });
+        }
+
+        // guess we'd better do the math now...
+        return scratch.done({
+            // a closest = lambdaA * Aa + lambdaB * Ba
+            a: A.clone( last.a ).mult( lambdaA ).vadd( L.clone( prev.a ).mult( lambdaB ) ).values(),
+            // b closest = lambdaA * Ab + lambdaB * Bb
+            b: A.clone( last.b ).mult( lambdaA ).vadd( L.clone( prev.b ).mult( lambdaB ) ).values()
+        });
+    };
+
+    /**
+     * Physics.gjk( support(axis)[, seed, checkOverlapOnly, debugFn] ) -> Object
+     * - support (Function): The support function. Must return an object containing
+       the witness points (`.a`, `.b`) and the support point (`.pt`).
+       Recommended to use simple objects.
+       Eg:
+       ```javascript
+       return {
+            a: { x: 1, y:2 },
+            b: { x: 3, y: 4 },
+            pt: { x: 2, y: 2 }
+       };
+       ```
+     * - axis (Physics.vector): The axis to search
+     * - seed (Physics.vector): The starting direction for the simplex (defaults to x-axis)
+     * - checkOverlapOnly (Boolean): only check whether there is an overlap, don't calculate the depth
+     * - debugFn (Function): For debugging. Called at every iteration with the current simplex.
+     *
+     * Implementation agnostic GJK function.
+     *
+     * Gilbert–Johnson–Keerthi object collison algorithm
+     * For general information about GJK see:
+     * - [www.codezealot.org/archives/88](http://www.codezealot.org/archives/88)
+     * - [mollyrocket.com/849](http://mollyrocket.com/849)
+     *
+     * The algorithm information returned:
+     * ```javascript
+     * {
+     *     overlap: Boolean,
+     *     simplex: [] // array containing simplex points as simple x/y objects
+     * }
+     * ```
+     **/
+    var gjk = function gjk( support, seed, checkOverlapOnly, debugFn ){
+
+        var overlap = false
+            ,noOverlap = false // if we're sure we're not overlapping
+            ,distance = false
+            ,simplex = []
+            ,simplexLen = 1
+            // setup a scratchpad of temporary cheap objects
+            ,scratch = Physics.scratchpad()
+            // use seed as starting direction or use x axis
+            ,dir = scratch.vector().clone(seed || Physics.vector.axis[ 0 ])
+            ,last = scratch.vector()
+            ,lastlast = scratch.vector()
+            // some temp vectors
+            ,v1 = scratch.vector()
+            ,v2 = scratch.vector()
+            ,ab
+            ,ac
+            ,sign
+            ,tmp
+            ,iterations = 0
+            ;
+
+        // get the first Minkowski Difference point
+        tmp = support( dir );
+        simplexLen = simplex.push( tmp );
+        last.clone( tmp.pt );
+        // negate d for the next point
+        dir.negate();
+
+        // start looping
+        while ( ++iterations ) {
+
+            // swap last and lastlast, to save on memory/speed
+            last.swap(lastlast);
+            // push a new point to the simplex because we haven't terminated yet
+            tmp = support( dir );
+            simplexLen = simplex.push( tmp );
+            last.clone( tmp.pt );
+
+            if ( debugFn ){
+                debugFn( simplex );
+            }
+
+            if ( last.equals(Physics.vector.zero) ){
+                // we happened to pick the origin as a support point... lucky.
+                overlap = true;
+                break;
+            }
+
+            // check if the last point we added actually passed the origin
+            if ( !noOverlap && last.dot( dir ) <= 0.0 ) {
+                // if the point added last was not past the origin in the direction of d
+                // then the Minkowski difference cannot possibly contain the origin since
+                // the last point added is on the edge of the Minkowski Difference
+
+                // if we just need the overlap...
+                if ( checkOverlapOnly ){
+                    break;
+                }
+
+                noOverlap = true;
+            }
+
+            // if it's a line...
+            if ( simplexLen === 2 ){
+
+                // otherwise we need to determine if the origin is in
+                // the current simplex and act accordingly
+
+                dir = getNextSearchDir( last, lastlast, dir );
+                // continue...
+
+            // if it's a triangle... and we're looking for the distance
+            } else if ( noOverlap ){
+
+                // if we know there isn't any overlap and
+                // we're just trying to find the distance...
+                // make sure we're getting closer to the origin
+                dir.normalize();
+                tmp = lastlast.dot( dir );
+                if ( Math.abs(tmp - last.dot( dir )) < gjkAccuracy ){
+
+                    distance = -tmp;
+                    break;
+                }
+
+                // if we are still getting closer then only keep
+                // the points in the simplex that are closest to
+                // the origin (we already know that last is closer
+                // than the previous two)
+                // the norm is the same as distance(origin, a)
+                // use norm squared to avoid the sqrt operations
+                if (lastlast.normSq() < v1.clone(simplex[ 0 ].pt).normSq()) {
+
+                    simplex.shift();
+
+                } else {
+
+                    simplex.splice(1, 1);
+                }
+
+                dir = getNextSearchDir( v1.clone(simplex[ 1 ].pt), v2.clone(simplex[ 0 ].pt), dir );
+                // continue...
+
+            // if it's a triangle
+            } else {
+
+                // we need to trim the useless point...
+
+                ab = ab || scratch.vector();
+                ac = ac || scratch.vector();
+
+                // get the edges AB and AC
+                ab.clone( lastlast ).vsub( last );
+                ac.clone( simplex[ 0 ].pt ).vsub( last );
+
+                // here normally people think about this as getting outward facing
+                // normals and checking dot products. Since we're in 2D
+                // we can be clever...
+                sign = ab.cross( ac ) > 0;
+
+                if ( sign ^ (last.cross( ab ) > 0) ){
+
+                    // ok... so there's an XOR here... don't freak out
+                    // remember last = A = -AO
+                    // if AB cross AC and AO cross AB have the same sign
+                    // then the origin is along the outward facing normal of AB
+                    // so if AB cross AC and A cross AB have _different_ (XOR) signs
+                    // then this is also the case... so we proceed...
+
+                    // point C is dead to us now...
+                    simplex.shift();
+
+                    // if we haven't deduced that we've enclosed the origin
+                    // then we know which way to look...
+                    // morph the ab vector into its outward facing normal
+                    ab.perp( !sign );
+
+                    // swap
+                    dir.swap( ab );
+
+                    // continue...
+
+                    // if we get to this if, then it means we can continue to look along
+                    // the other outward normal direction (ACperp)
+                    // if we don't see the origin... then we must have it enclosed
+                } else if ( sign ^ (ac.cross( last ) > 0) ){
+                    // then the origin is along the outward facing normal
+                    // of AC; (ACperp)
+
+                    // point B is dead to us now...
+                    simplex.splice(1, 1);
+
+                    ac.perp( sign );
+
+                    // swap
+                    dir.swap( ab );
+
+                    // continue...
+
+                } else {
+
+                    // we have enclosed the origin!
+                    overlap = true;
+                    // fewf... take a break
+                    break;
+                }
+            }
+
+            // woah nelly... that's a lot of iterations.
+            // Stop it!
+            if (iterations > gjkMaxIterations){
+                scratch.done();
+                return {
+                    simplex: simplex,
+                    iterations: iterations,
+                    distance: 0,
+                    maxIterationsReached: true
+                };
+            }
+        }
+
+        // free workspace
+        scratch.done();
+
+        tmp = {
+            overlap: overlap,
+            simplex: simplex,
+            iterations: iterations
+        };
+
+        if ( distance !== false ){
+
+            tmp.distance = distance;
+            tmp.closest = getClosestPoints( simplex );
+        }
+
+        return tmp;
+    };
+
+    Physics.gjk = gjk;
+
+})();
+
+
+// ---
+// inside: src/math/statistics.js
+
+(function(){
+
+    Physics.statistics = {
+        /**
+         * Physics.statistics.pushRunningAvg( v, k, m, s ) -> Array
+         * - v (Number): is value to push
+         * - k (Number): is num elements
+         * - m (Number): is current mean
+         * - s (Number): is current s value
+         * + (Array): Returns a 2 element array containing the next mean, and s value
+         *
+         * Push a value to a running average calculation.
+         * see [http://www.johndcook.com/blog/standard_deviation]
+         *
+         * Note: variance can be calculated from the "s" value by multiplying it by `1/(k-1)`
+         **/
+        pushRunningAvg: function( v, k, m, s ){
+
+            var x = v - m;
+
+            // Mk = Mk-1+ (xk – Mk-1)/k
+            // Sk = Sk-1 + (xk – Mk-1)*(xk – Mk).
+            m += x / k;
+            s += x * (v - m);
+            return [m, s];
+        },
+
+        /**
+        * Physics.statistics.pushRunningVectorAvg( v, k, m[, s] )
+        * - v (Physics.vector): is vector to push
+        * - k (Number): is num elements
+        * - m (Physics.vector): is current mean
+        * - s (Physics.vector): is current s value
+        *
+        * Push a vector to a running vector average calculation.
+        * see [http://www.johndcook.com/blog/standard_deviation]
+        *
+        * Calculations are done in place. The `m` and `s` parameters are altered.
+        *
+        * Note: variance can be calculated from the "s" vector by multiplying it by `1/(k-1)`
+        *
+        * If s value is ommitted it won't be used.
+        **/
+        pushRunningVectorAvg: function( v, k, m, s ){
+            var invK = 1/k
+                ,x = v.get(0) - m.get(0)
+                ,y = v.get(1) - m.get(1)
+                ;
+
+            // Mk = Mk-1+ (xk – Mk-1)/k
+            // Sk = Sk-1 + (xk – Mk-1)*(xk – Mk).
+            m.add( x * invK, y * invK );
+
+            if ( s ){
+                x *= v.get(0) - m.get(0);
+                y *= v.get(1) - m.get(1);
+
+                s.add( x, y );
+            }
+        }
+    };
+})();
+
+
+// ---
+// inside: src/math/transform.js
+
+(function(){
+    
+    /**
+     * class Physics.transform
+     * 
+     * Vector Transformations class for rotating and translating vectors
+     **/
+
+    /**
+     * new Physics.transform( [vect, angle, origin] )
+     * new Physics.transform( transform )
+     * - vect (Vectorish): Translation vector
+     * - transform (Physics.transform): Transform to copy
+     * - angle (Number): Angle (radians) to use for rotation
+     * - origin (Vectorish): Origin of the rotation
+     * 
+     * Transform Constructor / Factory
+     **/
+    var Transform = function Transform( vect, angle, origin ) {
+
+        if (!(this instanceof Transform)){
+            return new Transform( vect, angle );
+        }
+
+        this.v = new Physics.vector();
+        this.o = new Physics.vector(); // origin of rotation
+        
+        if ( vect instanceof Transform ){
+
+            this.clone( vect );
+            return;
+        }
+
+        if (vect){
+            this.setTranslation( vect );
+        }
+
+        this.setRotation( angle || 0, origin );
+    };
+
+    /**
+     * Physics.transform#setTranslation( vect ) -> this
+     * - vect (Vectorish): The translation vector
+     * 
+     * Set the translation portion of the transform.
+     **/
+    Transform.prototype.setTranslation = function( vect ){
+
+        this.v.clone( vect );
+        return this;
+    };
+
+    /**
+     * Physics.transform#setRotation( angle[, origin ] ) -> this
+     * - angle (Number): Angle (radians) to use for rotation
+     * - origin (Vectorish): Origin of the rotation
+     *
+     * Set the rotation portion of the transform
+     **/
+    Transform.prototype.setRotation = function( angle, origin ){
+
+        this.cosA = Math.cos( angle );
+        this.sinA = Math.sin( angle );
+
+        if ( origin ){
+            this.o.clone( origin );
+        } else {
+            this.o.zero();
+        }
+
+        return this;
+    };
+
+    /**
+     * Physics.transform#clone( [transform] ) -> this|Physics.transform
+     * - transform (Physics.transform): Transform to copy
+     * + (this): For chaining
+     * + (Physics.transform): New copy of `this` if none is specified as an argument
+     * 
+     * Clone another transform. Or clone self into new transform.
+     **/
+    Transform.prototype.clone = function( t ){
+
+        if ( t ){
+
+            this.setTranslation( t.v );
+            this.cosA = t.cosA;
+            this.sinA = t.sinA;
+            this.o.clone( t.o );
+
+            return this;
+        }
+
+        return new Transform( this );
+    };
+
+    Physics.transform = Transform;
+
+})();
+
+// ---
+// inside: src/math/vector.js
+
+(function(window){
+
+    // http://jsperf.com/vector-storage-test/2
+
+    // cached math functions
+    // TODO: might be faster not to do this???
+    var sqrt = Math.sqrt
+        ,min = Math.min
+        ,max = Math.max
+        ,acos = Math.acos
+        ,atan2 = Math.atan2
+        ,TWOPI = Math.PI * 2
+        ,typedArrays = !!window.Float64Array
+        ;
+
+    /**
+     * class Physics.vector
+     *
+     * The vector class and factory function.
+     *
+     * Call `Physics.vector` with the same arguments as
+     * [[new Physics.vector]] to create an instance.
+     *
+     * The vector methods mostly modify the vector instance.
+     * This makes computations faster because creating vectors
+     * is avoided.
+     *
+     * Creating vectors is generally an expensive operation
+     * so try to avoid doing this in the simulation loop.
+     * Instead you can use [[Physics.scratchpad]] to get
+     * temporary vectors for use in performance critical
+     * code.
+     *
+     * _Note_: The coordinate system is left-handed, meaning that
+     * the clockwise angular direction is positive. This has implications
+     * for the cross-product rule.
+     **/
+
+    /** section: Special
+     * class Vectorish
+     *
+     * Any object with `.x` and `.y` properties.
+     *
+     * A `Vectorish` isn't really a class. In this documentation, when
+     * an argument is specified as a `Vectorish` it means either a true
+     * [[Physics.vector]] instance, or an object literal with `.x` and `.y`
+     * properties.
+     **/
+
+    /**
+     * new Physics.vector( x, y )
+     * new Physics.vector( vect )
+     * - x (Number): The x coordinate
+     * - y (Number): The y coordinate
+     * - vect (Vectorish): A vector-like object to clone
+     *
+     * Vector Constructor.
+     **/
+    var Vector = function Vector( x, y ) {
+
+        // enforce instantiation
+        if ( !(this instanceof Vector) ){
+
+            return new Vector( x, y );
+        }
+
+        // arrays to store values
+        // x = _[0]
+        // y = _[1]
+        // norm = _[3]
+        // normsq = _[4]
+
+        /** internal
+         * Physics.vector#_
+         *
+         * Private storage array for data.
+         *
+         * Do not access this directly. Private. Keep out.
+         **/
+        if (typedArrays){
+            this._ = new Float64Array(5);
+        } else {
+            this._ = [];
+        }
+
+        if (x && (x.x !== undefined || x._ && x._.length)){
+
+            this.clone( x );
+
+        } else {
+
+            this.recalc = true; //whether or not recalculate norms
+            this.set( x, y );
+        }
+    };
+
+    Object.defineProperties( Vector.prototype, {
+        /**
+         * Physics.vector#x
+         *
+         * Getter/setter property for the x coordinate.
+         **/
+        x: {
+            get: function(){
+                return +this._[0];
+            },
+            set: function( x ){
+                x = +x || 0;
+                this.recalc = ( x === this._[0] );
+                this._[0] = x;
+            }
+        },
+        /**
+         * Physics.vector#y
+         *
+         * Getter/setter property for the y coordinate.
+         **/
+        y: {
+            get: function(){
+                return +this._[1];
+            },
+            set: function( y ){
+                y = +y || 0;
+                this.recalc = ( y === this._[1] );
+                this._[1] = y;
+            }
+        }
+    });
+
+    //
+    // Methods
+    //
+
+    /**
+     * Physics.vector#set( x, y ) -> this
+     * - x (Number): x coordinate
+     * - y (Number): y coordinate
+     *
+     * Sets the x and y components of this vector.
+     **/
+    Vector.prototype.set = function( x, y ) {
+
+        this.recalc = true;
+
+        this._[0] = +x || 0;
+        this._[1] = +y || 0;
+        return this;
+    };
+
+    /** deprecated: 0.6.0..1.0.0
+     * Physics.vector#get( idx ) -> Number
+     * - idx (Number): The coordinate index (0 or 1)
+     *
+     * Get the x or y component by index.
+     **/
+    Vector.prototype.get = function( n ){
+
+        return this._[ n ];
+    };
+
+    /**
+     * Physics.vector#vadd( v ) -> this
+     * - v (Physics.vector): vector to add
+     *
+     * Add a [[Physics.vector]] to `this`.
+     **/
+    Vector.prototype.vadd = function( v ) {
+
+        this.recalc = true;
+
+        this._[0] += v._[0];
+        this._[1] += v._[1];
+        return this;
+    };
+
+    /**
+     * Physics.vector#vsub( v ) -> this
+     * - v (Physics.vector): vector to subtract
+     *
+     * Subtract a [[Physics.vector]] from `this`.
+     **/
+    Vector.prototype.vsub = function( v ) {
+
+        this.recalc = true;
+
+        this._[0] -= v._[0];
+        this._[1] -= v._[1];
+        return this;
+    };
+
+    /**
+     * Physics.vector#add( x, y ) -> this
+     * - x (Number): amount to add to the x coordinate
+     * - y (Number): amount to add to the y coordinate
+     *
+     * Add scalars [[Physics.vector]] to the coordinates.
+     **/
+    Vector.prototype.add = function( x, y ){
+
+        this.recalc = true;
+
+        this._[0] += +x || 0;
+        this._[1] += +y || 0;
+        return this;
+    };
+
+    /**
+     * Physics.vector#sub( x, y ) -> this
+     * - x (Number): amount to subtract from the x coordinate
+     * - y (Number): amount to subtract from the y coordinate
+     *
+     * Subtract scalars [[Physics.vector]] from the coordinates.
+     **/
+    Vector.prototype.sub = function( x, y ){
+
+        this.recalc = true;
+
+        this._[0] -= x;
+        this._[1] -= y === undefined? 0 : y;
+        return this;
+    };
+
+    /**
+     * Physics.vector#mult( m ) -> this
+     * - m (Number): amount to multiply this vector by
+     *
+     * Multiply this by a scalar quantity.
+     *
+     * Same as scaling the vector by an amount `m`.
+     **/
+    Vector.prototype.mult = function( m ) {
+
+        if ( !this.recalc ){
+
+            this._[4] *= m * m;
+            this._[3] *= m;
+        }
+
+        this._[0] *= m;
+        this._[1] *= m;
+        return this;
+    };
+
+    /**
+     * Physics.vector#dot( v ) -> Number
+     * - v (Physics.vector): The other vector
+     *
+     * Compute the dot product of this vector with `v`.
+     **/
+    Vector.prototype.dot = function( v ) {
+
+        return (this._[0] * v._[0]) + (this._[1] * v._[1]);
+    };
+
+    /**
+     * Physics.vector#cross( v ) -> Number
+     * - v (Physics.vector): The other vector
+     *
+     * Compute the (left-handed) cross product of this vector with `v`.
+     **/
+    Vector.prototype.cross = function( v ) {
+
+        return ( - this._[0] * v._[1]) + (this._[1] * v._[0]);
+    };
+
+    /**
+     * Physics.vector#proj( v ) -> Number
+     * - v (Physics.vector): The other vector
+     *
+     * Compute the [scalar projection](http://en.wikipedia.org/wiki/Vector_projection#Scalar_projection_2) of this along `v`.
+     **/
+    Vector.prototype.proj = function( v ){
+
+        return this.dot( v ) / v.norm();
+    };
+
+
+    /**
+     * Physics.vector#vproj( v ) -> this
+     * - v (Physics.vector): The other vector
+     *
+     * Compute the [vector projection](http://en.wikipedia.org/wiki/Vector_projection#Vector_projection_2) of this along `v` and copy the result into this vector.
+     **/
+    Vector.prototype.vproj = function( v ){
+
+        var m = this.dot( v ) / v.normSq();
+        return this.clone( v ).mult( m );
+    };
+
+    /**
+     * Physics.vector#angle( [v] ) -> Number
+     * - v (Physics.vector): The other vector
+     * + (Number): The angle in radians between this vector and the x-axis OR `v` if specified
+     *
+     * Compute the angle between `this` and vector `v` or this and x axis.
+     **/
+    Vector.prototype.angle = function( v ){
+
+        var ang;
+
+        if ( this.equals( Vector.zero ) ){
+
+            if ( v ){
+                return v.angle();
+            } else {
+                return NaN;
+            }
+
+        } else {
+
+            if ( v && !v.equals( Vector.zero ) ){
+                ang = atan2( this._[1] * v._[0] - this._[0] * v._[1], this._[0] * v._[0] + this._[1] * v._[1]);
+            } else {
+                ang = atan2( this._[ 1 ], this._[ 0 ] );
+            }
+        }
+
+        while (ang > Math.PI){
+            ang -= TWOPI;
+        }
+
+        while (ang < -Math.PI){
+            ang += TWOPI;
+        }
+
+        return ang;
+    };
+
+    /**
+     * Physics.vector#angle2( left, right ) -> Number
+     * - left (Physics.vector): The position on the left
+     * - right (Physics.vector): The position on the right
+     *
+     * Compute the angle created between three points; left -> this -> right.
+     **/
+    Vector.prototype.angle2 = function( left, right ){
+
+        var x1 = left._[0] - this._[0]
+            ,y1 = left._[1] - this._[1]
+            ,x2 = right._[0] - this._[0]
+            ,y2 = right._[1] - this._[1]
+            ,ang = atan2( y1 * x2 - x1 * y2, x1 * x2 + y1 * y2)
+            ;
+
+        while (ang > Math.PI){
+            ang -= TWOPI;
+        }
+
+        while (ang < -Math.PI){
+            ang += TWOPI;
+        }
+
+        return ang;
+    };
+
+    /**
+     * Physics.vector#norm() -> Number
+     *
+     * Compute the norm (length) of this vector.
+     **/
+    Vector.prototype.norm = function() {
+
+        if (this.recalc){
+            this.recalc = false;
+            this._[4] = (this._[0] * this._[0] + this._[1] * this._[1]);
+            this._[3] = sqrt( this._[4] );
+        }
+
+        return this._[3];
+    };
+
+    /**
+     * Physics.vector#normSq() -> Number
+     *
+     * Compute the norm (length) squared of this vector.
+     **/
+    Vector.prototype.normSq = function() {
+
+        if (this.recalc){
+            this.recalc = false;
+            this._[4] = (this._[0] * this._[0] + this._[1] * this._[1]);
+            this._[3] = sqrt( this._[4] );
+        }
+
+        return this._[4];
+    };
+
+    /**
+     * Physics.vector#dist( v ) -> Number
+     * - v (Physics.vector): The other vector
+     *
+     * Compute the distance from this vector to another vector `v`.
+     **/
+    Vector.prototype.dist = function( v ) {
+
+        var dx, dy;
+        return sqrt(
+            (dx = (v._[0] - this._[0])) * dx +
+            (dy = (v._[1] - this._[1])) * dy
+        );
+    };
+
+    /**
+     * Physics.vector#distSq( v ) -> Number
+     * - v (Physics.vector): The other vector
+     *
+     * Compute the distance squared from this vector to another vector `v`.
+     **/
+    Vector.prototype.distSq = function( v ) {
+
+        var dx, dy;
+        return (
+            (dx = (v._[0] - this._[0])) * dx +
+            (dy = (v._[1] - this._[1])) * dy
+        );
+    };
+
+    /**
+     * Physics.vector#perp( [ccw] ) -> this
+     * - ccw (Boolean): flag to indicate that we should rotate counterclockwise
+     *
+     * Change this vector into a vector that will be perpendicular.
+     *
+     * In other words, rotate by (+-) 90 degrees.
+     **/
+    Vector.prototype.perp = function( ccw ) {
+
+        var tmp = this._[0]
+            ;
+
+        if ( ccw ){
+
+            // x <-> y
+            // negate y
+            this._[0] = this._[1];
+            this._[1] = -tmp;
+
+        } else {
+
+            // x <-> y
+            // negate x
+            this._[0] = -this._[1];
+            this._[1] = tmp;
+        }
+
+        return this;
+    };
+
+    /**
+     * Physics.vector#normalize() -> this
+     *
+     * Normalise this vector, making it a unit vector.
+     **/
+    Vector.prototype.normalize = function() {
+
+        var m = this.norm();
+
+        // means it's a zero Vector
+        if ( m === 0 ){
+            return this;
+        }
+
+        m = 1/m;
+
+        this._[0] *= m;
+        this._[1] *= m;
+
+        this._[3] = 1.0;
+        this._[4] = 1.0;
+
+        return this;
+    };
+
+    /**
+     * Physics.vector#transform( t ) -> this
+     * - t (Physics.transform): The transformation to apply
+     *
+     * Apply a [[Physics.transform]] to this vector.
+     **/
+    Vector.prototype.transform = function( t ){
+
+        var sinA = t.sinA
+            ,cosA = t.cosA
+            ,x = t.o._[ 0 ]
+            ,y = t.o._[ 1 ]
+            ;
+
+        this._[ 0 ] -= x;
+        this._[ 1 ] -= y;
+
+        // rotate about origin "o" then translate
+        return this.set(
+            this._[ 0 ] * cosA - this._[ 1 ] * sinA + x + t.v._[ 0 ],
+            this._[ 0 ] * sinA + this._[ 1 ] * cosA + y + t.v._[ 1 ]
+        );
+    };
+
+    /**
+     * Physics.vector#transformInv( t ) -> this
+     * - t (Physics.transform): The transformation to apply the inverse of
+     *
+     * Apply an inverse [[Physics.transform]] to this vector.
+     **/
+    Vector.prototype.transformInv = function( t ){
+
+        var sinA = t.sinA
+            ,cosA = t.cosA
+            ,x = t.o._[ 0 ]
+            ,y = t.o._[ 1 ]
+            ;
+
+        this._[ 0 ] -= x + t.v._[ 0 ];
+        this._[ 1 ] -= y + t.v._[ 1 ];
+
+        // inverse translate then inverse rotate about origin "o"
+        return this.set(
+            this._[ 0 ] * cosA + this._[ 1 ] * sinA + x,
+            - this._[ 0 ] * sinA + this._[ 1 ] * cosA + y
+        );
+    };
+
+    /**
+     * Physics.vector#rotate( t ) -> this
+     * Physics.vector#rotate( ang[, o] ) -> this
+     * - t (Physics.transform): The transformation to apply the rotational part of
+     * - ang (Number): The angle (in radians), to rotate by
+     * - o (Vectorish): The point of origin of the rotation
+     *
+     * Rotate this vector.
+     *
+     * An angle and rotation origin can be specified,
+     * or a transform can be specified and only the rotation
+     * portion of that transform will be applied
+     **/
+    Vector.prototype.rotate = function( t, o ){
+
+        var sinA
+            ,cosA
+            ,x = 0
+            ,y = 0
+            ;
+
+        if ( typeof t === 'number' ){
+            sinA = Math.sin( t );
+            cosA = Math.cos( t );
+
+            if ( o ){
+                x = o.x;
+                y = o.y;
+            }
+        } else {
+            sinA = t.sinA;
+            cosA = t.cosA;
+
+            x = t.o._[ 0 ];
+            y = t.o._[ 1 ];
+        }
+
+        this._[ 0 ] -= x;
+        this._[ 1 ] -= y;
+
+        return this.set(
+            this._[ 0 ] * cosA - this._[ 1 ] * sinA + x,
+            this._[ 0 ] * sinA + this._[ 1 ] * cosA + y
+        );
+    };
+
+    /**
+     * Physics.vector#rotateInv( t ) -> this
+     * - t (Physics.transform): The transformation to apply the inverse rotational part of
+     *
+     * Apply the inverse rotation of a transform.
+     *
+     * Only the inverse rotation portion of
+     * that transform will be applied.
+     **/
+    Vector.prototype.rotateInv = function( t ){
+
+        return this.set(
+            (this._[ 0 ] - t.o._[ 0 ]) * t.cosA + (this._[ 1 ] - t.o._[ 1 ]) * t.sinA + t.o._[ 0 ],
+            -(this._[ 0 ] - t.o._[ 0 ]) * t.sinA + (this._[ 1 ] - t.o._[ 1 ]) * t.cosA + t.o._[ 1 ]
+        );
+    };
+
+    /**
+     * Physics.vector#translate( t ) -> this
+     * - t (Physics.transform): The transformation to apply the translational part of
+     *
+     * Apply the translation of a transform.
+     *
+     * Only the translation portion of
+     * that transform will be applied.
+     **/
+    Vector.prototype.translate = function( t ){
+
+        return this.vadd( t.v );
+    };
+
+    /**
+     * Physics.vector#translateInv( t ) -> this
+     * - t (Physics.transform): The transformation to apply the inverse translational part of
+     *
+     * Apply the inverse translation of a transform.
+     *
+     * Only the inverse translation portion of
+     * that transform will be applied.
+     **/
+    Vector.prototype.translateInv = function( t ){
+
+        return this.vsub( t.v );
+    };
+
+
+    /**
+     * Physics.vector#clone( [v] ) -> this|Physics.vector
+     * - v (Vectorish): The vector-like object to clone
+     * + (this): If `v` is specified as an argument
+     * + (Physics.vector): A new vector instance that clones this vector, if no argument is specified
+     *
+     * Create a clone of this vector, or clone another vector into this instance.
+     *
+     * This is especially useful in vector algorithms
+     * that use temporary vectors (which most should).
+     * You can create temporary vectors and then do things like...
+     * ```
+     * temp.clone( otherVector );
+     * // compute things with temp...
+     * // then save the result
+     * result.clone( tmp );
+     * ```
+     **/
+    Vector.prototype.clone = function( v ) {
+
+        // http://jsperf.com/vector-storage-test
+
+        if ( v ){
+
+            if (!v._){
+
+                return this.set( v.x, v.y );
+            }
+
+            this.recalc = v.recalc;
+
+            if (!v.recalc){
+                this._[3] = v._[3];
+                this._[4] = v._[4];
+            }
+
+            this._[0] = v._[0];
+            this._[1] = v._[1];
+
+            return this;
+        }
+
+        return new Vector( this );
+    };
+
+    /**
+     * Physics.vector#swap( v ) -> this
+     * - v (Physics.vector): The other vector
+     *
+     * Swap values with other vector.
+     **/
+    Vector.prototype.swap = function( v ){
+
+        var _ = this._;
+        this._ = v._;
+        v._ = _;
+
+        _ = this.recalc;
+        this.recalc = v.recalc;
+        v.recalc = _;
+        return this;
+    };
+
+    /**
+     * Physics.vector#values() -> Object
+     *
+     * Get the coordinate values as an object literal.
+     **/
+    Vector.prototype.values = function(){
+
+        return {
+            x: this._[0],
+            y: this._[1]
+        };
+    };
+
+
+    /**
+     * Physics.vector#zero() -> this
+     *
+     * Set the coordinates of this vector to zero.
+     **/
+    Vector.prototype.zero = function() {
+
+        this._[3] = 0.0;
+        this._[4] = 0.0;
+
+        this._[0] = 0.0;
+        this._[1] = 0.0;
+        return this;
+    };
+
+    /**
+     * Physics.vector#negate() -> this
+     *
+     * Flip this vector in the opposite direction.
+     **/
+    Vector.prototype.negate = function( component ){
+
+        if (component !== undefined){
+
+            this._[ component ] = -this._[ component ];
+            return this;
+        }
+
+        this._[0] = -this._[0];
+        this._[1] = -this._[1];
+        return this;
+    };
+
+    /**
+     * Physics.vector#clamp( minV, maxV ) -> this
+     * - minV (Vectorish): The minimum vector
+     * - maxV (Vectorish): The maximum vector
+     *
+     * Constrain vector components to minima and maxima.
+     *
+     * The vector analog of [scalar clamping](http://en.wikipedia.org/wiki/Clamping_(graphics)).
+     **/
+    Vector.prototype.clamp = function( minV, maxV ){
+
+        this._[0] = min(max(this._[0], minV.x), maxV.x);
+        this._[1] = min(max(this._[1], minV.y), maxV.y);
+        this.recalc = true;
+        return this;
+    };
+
+    /**
+     * Physics.vector#toString() -> String
+     *
+     * Get a formatted string of this vector's coordinates.
+     **/
+    Vector.prototype.toString = function(){
+
+        return '('+this._[0] + ', ' + this._[1]+')';
+    };
+
+
+    /**
+     * Physics.vector#equals( v ) -> Boolean
+     * - v (Physics.vector): The other vector
+     *
+     * Determine if this vector equals another.
+     **/
+    Vector.prototype.equals = function( v ){
+
+        return this._[0] === v._[0] &&
+            this._[1] === v._[1] &&
+            this._[2] === v._[2];
+    };
+
+    /**
+     * Physics.vector.axis = Array
+     *
+     * Read-only axis vectors for general reference.
+     *
+     * Example:
+     *
+     * ```javascript
+     * Physics.vector.axis[0]; // The x axis unit vector
+     * Physics.vector.axis[1]; // The y axis unit vector
+     * ```
+     **/
+    Vector.axis = [
+        new Vector(1.0, 0.0),
+        new Vector(0.0, 1.0)
+    ];
+
+    /**
+     * Physics.vector.zero = zeroVector
+     *
+     * Read-only zero vector for reference
+     **/
+    Vector.zero = new Vector(0, 0);
+
+    // assign
+    Physics.vector = Vector;
+
+}(this)); // end Vector class
+
+
+// ---
+// inside: src/util/noconflict.js
+
+(function( window ){
+
+    var _Physics = window.Physics;
+
+    /**
+     * Physics.noConflict() -> Physics
+     * 
+     * Restore the original reference to the global window.Physics variable.
+     * 
+     * Does nothing if PhysicsJS doesn't have a reference in global scope
+     **/
+    Physics.noConflict = function(){
+
+        if ( window.Physics === Physics ) {
+            window.Physics = _Physics;
+        }
+        
+        return Physics;
+    };
+
+})( this );
+
+// ---
+// inside: src/util/decorator.js
+
+/** related to: factory
+ * Physics.util.decorator( type [, protoDef ] ) -> Function
+ * - type (String): The name of the factory you are creating
+ * - protoDef (Object): The top-level prototype
+ * + (Function): The factory function
+ *
+ * Facilitates creation of decorator factory functions.
+ *
+ * See the [[factory]] definition for the factory signatures.
+ * [For full documentation and examples, please visit the wiki](https://github.com/wellcaffeinated/PhysicsJS/wiki/Fundamentals#the-factory-pattern).
+ *
+ * Example:
+ *
+ * ```javascript
+ * var factory = Physics.util.decorator('factory', {
+ *      // prototype methods...
+ *      method: function( args ){
+ *      }
+ * });
+ *
+ * // define
+ * factory( 'name', 'parent-name', function( parent ){
+ *
+ *      // extend further...
+ *      return {
+ *          // overrides
+ *          init: function( cfg ){
+ *              parent.init.call(this, cfg);
+ *          }
+ *      };
+ * });
+ *
+ * // instantiate
+ * var options = { key: 'val' };
+ * var instance = factory( 'name', options );
+ * ```
+ **/
+var Decorator = Physics.util.decorator = function Decorator( type, baseProto ){
+
+    var registry = {}
+        ,proto = {}
+        ;
+
+    // extend that supports getters/setters
+    // only extends functions
+    var extend = function extend( to, from ){
+        var desc, key;
+        for ( key in from ){
+            desc = Object.getOwnPropertyDescriptor( from, key );
+            if ( desc.get || desc.set ){
+
+                Object.defineProperty( to, key, desc );
+
+            } else if ( Physics.util.isFunction( desc.value ) ){
+
+                to[ key ] = desc.value;
+            }
+        }
+        return to;
+    };
+
+    // http://ejohn.org/blog/objectgetprototypeof/
+    /* jshint -W103 */
+    var getProto = Object.getPrototypeOf;
+    if ( typeof getProto !== 'function' ) {
+        if ( typeof 'test'.__proto__ === 'object' ) {
+            getProto = function(object){
+                return object.__proto__;
+            };
+        } else {
+            getProto = function(object){
+                // May break if the constructor has been tampered with
+                return object.constructor.prototype;
+            };
+        }
+    }
+    /* jshint +W103 */
+
+    var objectCreate = Object.create;
+    if (typeof objectCreate !== 'function') {
+        objectCreate = function (o) {
+            function F() {}
+            F.prototype = o;
+            return new F();
+        };
+    }
+
+    /*
+     * mixin( key, val )
+     * mixin( obj )
+     * - key (String): The method name
+     * - val (Function): The function to assign
+     * - obj (Object): object with many `key: fn` pairs
+     *
+     * Apply mixin methods to decorator base.
+     */
+    var mixin = function mixin( key, val ){
+
+        if ( typeof key === 'object' ){
+            proto = extend(proto, key);
+            proto.type = type;
+            return;
+        }
+
+        if ( key !== 'type' && Physics.util.isFunction( val ) ){
+            proto[ key ] = val;
+        }
+    };
+
+    // @TODO: not sure of the best way to make the constructor names
+    // transparent and readable in debug consoles...
+    mixin( baseProto );
+
+    /**  belongs to: Physics.util.decorator
+     * factory( name[, parentName], decorator[, cfg] )
+     * factory( name, cfg ) -> Object
+     * -  name       (String):  The class name
+     * -  parentName (String): The name of parent class to extend
+     * -  decorator  (Function): The decorator function that should define and return methods to extend (decorate) the base class
+     * -  cfg        (Object): The configuration to pass to the class initializer
+     *
+     * Factory function for definition and instantiation of subclasses.
+     *
+     * Use the first signature (once) to define it first.
+     * If defining without the "cfg" parameter, void will be returned. Otherwise the class instance will be returned.
+     *
+     * See [[Physics.util.decorator]] for more information.
+     **/
+    var factory = function factory( name, parentName, decorator, cfg ){
+
+        var instance
+            ,result
+            ,parent = proto
+            ,tmp
+            ;
+
+        // set parent if specified
+        if ( typeof parentName !== 'string' ){
+
+            // ... otherwise reassign parameters
+            cfg = decorator;
+            decorator = parentName;
+
+        } else {
+
+            // extend the specified module
+            parent = registry[ parentName ];
+
+            if ( !parent ){
+
+                throw 'Error: "' + parentName + '" ' + type + ' not defined';
+            }
+
+            parent = parent.prototype;
+        }
+
+        if ( typeof decorator === 'function' ){
+
+            result = registry[ name ];
+
+            if ( result ){
+
+                result.prototype = extend(result.prototype, decorator( getProto(result.prototype) ));
+
+            } else {
+                // newly defined
+                // store the new class
+                result = registry[ name ] = function constructor( opts ){
+                    if (this.init){
+                        this.init( opts );
+                    }
+                };
+
+                result.prototype = objectCreate( parent );
+                result.prototype = extend(result.prototype, decorator( parent, result.prototype ));
+            }
+
+            result.prototype.type = type;
+            result.prototype.name = name;
+
+        } else {
+
+            cfg = decorator || {};
+            result = registry[ name ];
+            if (!result){
+
+                throw 'Error: "' + name + '" ' + type + ' not defined';
+            }
+        }
+
+        if ( cfg ) {
+
+            // create a new instance from the provided decorator
+            return new result( cfg );
+        }
+    };
+
+    factory.mixin = mixin;
+
+    return factory;
+};
+
+
+// ---
+// inside: src/util/helpers.js
+
+/**
+ * Physics.util.indexOf( arr, value ) -> Number
+ * - arr (Array): The array to search
+ * - value (Mixed): The value to find
+ * + (Number): The index of `value` in the array OR `-1` if not found
+ *
+ * Fast indexOf implementation.
+ **/
+Physics.util.indexOf = function indexOf(arr, value) {
+    var fr = 0, bk = arr.length;
+    while (fr < bk) {
+        bk--;
+        if (arr[ fr ] === value) {
+            return fr;
+        }
+        if (arr[ bk ] === value) {
+            return bk;
+        }
+        fr++;
+    }
+    return -1;
+};
+
+
+// http://jsperf.com/array-destroy/87
+/**
+ * Physics.util.clearArray( arr ) -> Array
+ * - arr (Array): The array to clear
+ * + (Array): The array passed in
+ *
+ * Quickly clear an array.
+ **/
+Physics.util.clearArray = function clearArray(arr){
+    var l = arr.length;
+    while( l-- ){
+        arr.pop();
+    }
+    return arr;
+};
+
+/**
+ * Physics.util.throttle( fn, delay ) -> Function
+ * - fn (Function): The function to throttle
+ * - delay (Number): Time in milliseconds
+ *
+ * Ensure a function is only called once every specified time span.
+ **/
+Physics.util.throttle = function throttle( fn, delay, scope ){
+    var to
+        ,call = false
+        ,args
+        ,cb = function(){
+            clearTimeout( to );
+            if ( call ){
+                call = false;
+                to = setTimeout(cb, delay);
+                fn.apply(scope, args);
+            } else {
+                to = false;
+            }
+        }
+        ;
+
+    scope = scope || null;
+
+    return function(){
+        call = true;
+        args = arguments;
+        if ( !to ){
+            cb();
+        }
+    };
+};
+
+/**
+ * Physics.util.options( def[, target] ) -> Function
+ * - def (Object): Default options to set
+ * - target (Object): Where to copy the options to. Defaults to the returned function.
+ * + (Function): The options function
+ *
+ * Options helper to keep track of options. Call it with a config object. Access options directly on the function.
+ *
+ * Example:
+ *
+ * ```javascript
+ * this.options = Physics.util.options({ foo: 'bar', opt: 'def' });
+ * this.options({ opt: 'myVal' });
+ *
+ * this.options.foo; // === 'bar'
+ * this.options.def; // === 'myVal'
+ *
+ * // can also change defaults later
+ * this.options.defaults({ foo: 'baz' });
+ *
+ * // can add a change callback
+ * this.options.onChange(function( opts ){
+ *     // some option changed
+ *     // opts is the target
+ * });
+ * ```
+ **/
+// deep copy callback to extend deeper into options
+var deepCopyFn = function( a, b ){
+
+    if ( Physics.util.isPlainObject( b ) ){
+
+        return Physics.util.extend({}, a, b, deepCopyFn );
+    }
+
+    return b !== undefined ? b : a;
+};
+Physics.util.options = function( def, target ){
+
+    var _def = {}
+        ,fn
+        ,callbacks = []
+        ;
+
+    // set options
+    fn = function fn( options, deep ){
+
+        Physics.util.extend(target, options, deep ? deepCopyFn : null);
+        for ( var i = 0, l = callbacks.length; i < l; ++i ){
+            callbacks[ i ]( target );
+        }
+        return target;
+    };
+
+    // add defaults
+    fn.defaults = function defaults( def, deep ){
+        Physics.util.extend( _def, def, deep ? deepCopyFn : null );
+        Physics.util.defaults( target, _def, deep ? deepCopyFn : null );
+        return _def;
+    };
+
+    fn.onChange = function( cb ){
+        callbacks.push( cb );
+    };
+
+    target = target || fn;
+
+    fn.defaults( def );
+
+    return fn;
+};
+
+/**
+ * Physics.util.pairHash( id1, id2 ) -> Number
+ * - id1 (Number): The id of the first thing
+ * - id2 (Number): The id of the second thing
+ * + (Number): A unique numeric hash (valid for values < 2^16)
+ *
+ * Generate a unique numeric hash from two input IDs.
+ *
+ * Useful for speedy indexing of pairs.
+ **/
+Physics.util.pairHash = function( id1, id2 ){
+    id1 = id1|0;
+    id2 = id2|0;
+
+    if ( (id1|0) === (id2|0) ){
+
+        return -1;
+    }
+
+    // valid for values < 2^16
+    return ((id1|0) > (id2|0) ?
+        (id1 << 16) | (id2 & 0xFFFF) :
+        (id2 << 16) | (id1 & 0xFFFF))|0
+        ;
+};
+
+/**
+ * Physics.util.bind( fn, scope[, args... ] ) -> Function
+ * - fn (Function): The function to bind scope to
+ * - scope (Object): The scope to give to `fn`
+ * - args (Mixed): Arguments to send to `fn`
+ *
+ * Bind a scope to a function.
+ *
+ * Basically the same functionality as [Function.prototype.bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
+ **/
+if ( !Function.prototype.bind ){
+    Physics.util.bind = function( fn, scope, args ){
+        args = Array.prototype.slice.call( arguments, 2 );
+        return function(){
+            return fn.apply( scope, args.concat( Array.prototype.slice.call(arguments) ) );
+        };
+    };
+} else {
+    Physics.util.bind = function( fn, scope, args ){
+        args = Array.prototype.slice.call( arguments, 1 );
+        return Function.prototype.bind.apply( fn, args );
+    };
+}
+
+/**
+ * Physics.util.find( collection, fn( value, index, collection ) ) -> Mixed
+ * - collection (Array): Collection of values to test
+ * - fn (Function): The test function
+ * - value (Mixed): The value to test
+ * - index (Number): The index of value in collection
+ * - collection (Array): The input collection
+ *
+ * Test an array of values against a test function
+ * and return the first value for which the function
+ * returns true.
+ **/
+Physics.util.find = function( collection, fn ){
+    var i
+        ,l = collection.length
+        ,val
+        ;
+
+    for ( i = 0; i < l; i++ ){
+        val = collection[ i ];
+        if ( fn( val, i, collection ) ){
+            return val;
+        }
+    }
+};
+
+/**
+ * Physics.util.filter( collection, fn( value, index, collection ) ) -> Array
+ * - collection (Array): Collection of values to test
+ * - fn (Function): The test function
+ * - value (Mixed): The value to test
+ * - index (Number): The index of value in collection
+ * - collection (Array): The input collection
+ *
+ * Test an array of values against a test function
+ * and return another array of values for which
+ * the test function returns true.
+ **/
+Physics.util.filter = function( collection, fn ){
+    var i
+        ,l = collection.length
+        ,val
+        ,matches = []
+        ;
+
+    for ( i = 0; i < l; i++ ){
+        val = collection[ i ];
+        if ( fn( val, i, collection ) ){
+            matches.push( val );
+        }
+    }
+
+    return matches;
+};
+
+// lodash methods
+
+(function(){
+/*
+ * @license
+ * Modified version of:
+ * Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/>
+ * Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
+ * Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
+ * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ * Available under MIT license <http://lodash.com/license>
+ */
+
+/* Used to determine if values are of the language type Object */
+var objectTypes = {
+  'boolean': false,
+  'function': true,
+  'object': true,
+  'number': false,
+  'string': false,
+  'undefined': false
+};
+var identity = function(a){ return a; };
+var arrayClass = '[object Array]';
+var objectClass = '[object Object]';
+var nativeKeys = Object.keys;
+var toString = Object.prototype.toString;
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+/* Used as the size when optimizations are enabled for large arrays */
+var largeArraySize = 75;
+/* Used to pool arrays and objects used internally */
+var arrayPool = [],
+    objectPool = [];
+/* Used as the max size of the `arrayPool` and `objectPool` */
+var maxPoolSize = 40;
+var keyPrefix = +new Date() + '';
+
+function releaseArray(array) {
+  Physics.util.clearArray( array );
+  if (arrayPool.length < maxPoolSize) {
+    arrayPool.push(array);
+  }
+}
+
+function releaseObject(object) {
+  var cache = object.cache;
+  if (cache) {
+    releaseObject(cache);
+  }
+  object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null;
+  if (objectPool.length < maxPoolSize) {
+    objectPool.push(object);
+  }
+}
+
+function getObject() {
+  return objectPool.pop() || {
+    'array': null,
+    'cache': null,
+    'criteria': null,
+    'false': false,
+    'index': 0,
+    'null': false,
+    'number': null,
+    'object': null,
+    'push': null,
+    'string': null,
+    'true': false,
+    'undefined': false,
+    'value': null
+  };
+}
+
+function getArray() {
+  return arrayPool.pop() || [];
+}
+
+function cacheIndexOf(cache, value) {
+  var type = typeof value;
+  cache = cache.cache;
+
+  if (type === 'boolean' || value == null) {
+    return cache[value] ? 0 : -1;
+  }
+  if (type !== 'number' && type !== 'string') {
+    type = 'object';
+  }
+  var key = type === 'number' ? value : keyPrefix + value;
+  cache = (cache = cache[type]) && cache[key];
+
+  return type === 'object' ?
+    (cache && Physics.util.indexOf(cache, value) > -1 ? 0 : -1) :
+    (cache ? 0 : -1);
+}
+
+function cachePush(value) {
+  var cache = this.cache,
+      type = typeof value;
+
+  if (type === 'boolean' || value == null) {
+    cache[value] = true;
+  } else {
+    if (type !== 'number' && type !== 'string') {
+      type = 'object';
+    }
+    var key = type === 'number' ? value : keyPrefix + value,
+        typeCache = cache[type] || (cache[type] = {});
+
+    if (type === 'object') {
+      (typeCache[key] || (typeCache[key] = [])).push(value);
+    } else {
+      typeCache[key] = true;
+    }
+  }
+}
+
+function createCache(array) {
+  var index = -1,
+      length = array.length,
+      first = array[0],
+      mid = array[(length / 2) | 0],
+      last = array[length - 1];
+
+  if (first && typeof first === 'object' &&
+      mid && typeof mid === 'object' && last && typeof last === 'object') {
+    return false;
+  }
+  var cache = getObject();
+  cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false;
+
+  var result = getObject();
+  result.array = array;
+  result.cache = cache;
+  result.push = cachePush;
+
+  while (++index < length) {
+    result.push(array[index]);
+  }
+  return result;
+}
+
+var shimKeys = function(object) {
+  var index, iterable = object, result = [];
+  if (!iterable){ return result; }
+  if (!(objectTypes[typeof object])){ return result; }
+    for (index in iterable) {
+      if (hasOwnProperty.call(iterable, index)) {
+        result.push(index);
+      }
+    }
+  return result;
+};
+
+var keys = !nativeKeys ? shimKeys : function(object) {
+  if (!Physics.util.isObject(object)) {
+    return [];
+  }
+  return nativeKeys(object);
+};
+
+var idCounter = 0;
+/**
+ * Physics.util.uniqueId( [prefix] ) -> String
+ * - prefix (String): Prefix to the id
+ *
+ * Generate a unique id, optionally prefixed.
+ **/
+Physics.util.uniqueId = function uniqueId(prefix) {
+    var id = ++idCounter;
+    return '' + (prefix || '') + id;
+};
+
+/*
+ * The base implementation of `_.random` without argument juggling or support
+ * for returning floating-point numbers.
+ *
+ * @private
+ * @param {number} min The minimum possible value.
+ * @param {number} max The maximum possible value.
+ * @returns {number} Returns a random number.
+ */
+function baseRandom(min, max) {
+    return min + Math.floor(Math.random() * (max - min + 1));
+}
+
+/*
+ * Creates an array of shuffled values, using a version of the Fisher-Yates
+ * shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to shuffle.
+ * @returns {Array} Returns a new shuffled collection.
+ * @example
+ *
+ * _.shuffle([1, 2, 3, 4, 5, 6]);
+ * // => [4, 1, 6, 3, 5, 2]
+ */
+Physics.util.shuffle = function(collection) {
+    var index = -1
+        ,length = collection ? collection.length : 0
+        ,result = Array(typeof length === 'number' ? length : 0)
+        ,i
+        ,l
+        ,value
+        ,rand
+        ;
+
+    for ( i = 0, l = collection.length; i < l; i++ ){
+        value = collection[ i ];
+        rand = baseRandom(0, ++index);
+        result[index] = result[rand];
+        result[rand] = value;
+    }
+    return result;
+};
+
+/**
+ * Physics.util.isObject( val ) -> Boolean
+ * - val (Mixed): The value to test
+ *
+ * Test if a value is an object.
+ **/
+Physics.util.isObject = function isObject(value) {
+    // check if the value is the ECMAScript language type of Object
+    // http://es5.github.io/#x8
+    // and avoid a V8 bug
+    // http://code.google.com/p/v8/issues/detail?id=2291
+    return !!(value && objectTypes[typeof value]);
+};
+
+function isFunction(value) {
+    return typeof value === 'function';
+}
+
+/**
+ * Physics.util.isFunction( val ) -> Boolean
+ * - val (Mixed): The value to test
+ *
+ * Test if a value is a function.
+ **/
+Physics.util.isFunction = isFunction;
+
+/**
+ * Physics.util.isArray( val ) -> Boolean
+ * - val (Mixed): The value to test
+ *
+ * Test if a value is an array.
+ **/
+Physics.util.isArray = Array.isArray || function(value) {
+  return value && typeof value === 'object' && typeof value.length === 'number' &&
+    toString.call(value) === arrayClass || false;
+};
+
+var reNative = RegExp('^' +
+  String(toString)
+    .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+    .replace(/toString| for [^\]]+/g, '.*?') + '$'
+);
+function isNative(value) {
+  return typeof value === 'function' && reNative.test(value);
+}
+
+function shimIsPlainObject(value) {
+  var ctor,
+      result;
+
+  // avoid non Object objects, `arguments` objects, and DOM elements
+  if (!(value && toString.call(value) === objectClass) ||
+      (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor))) {
+    return false;
+  }
+  // In most environments an object's own properties are iterated before
+  // its inherited properties. If the last iterated property is an object's
+  // own property then there are no inherited enumerable properties.
+  for (var key in value){
+    result = key;
+  }
+  return typeof result === 'undefined' || hasOwnProperty.call(value, result);
+}
+
+/**
+ * Physics.util.isPlainObject( val ) -> Boolean
+ * - val (Mixed): The value to test
+ *
+ * Test if a value is a plain javascript object.
+ **/
+Physics.util.isPlainObject = !Object.getPrototypeOf ? shimIsPlainObject : function(value) {
+  if (!(value && toString.call(value) === objectClass)) {
+    return false;
+  }
+  var valueOf = value.valueOf,
+      objProto = isNative(valueOf) && (objProto = Object.getPrototypeOf(valueOf)) && Object.getPrototypeOf(objProto);
+
+  return objProto ?
+    (value === objProto || Object.getPrototypeOf(value) === objProto) :
+    shimIsPlainObject(value);
+};
+
+function baseUniq(array, isSorted, callback) {
+  var index = -1,
+      indexOf = Physics.util.indexOf,
+      length = array ? array.length : 0,
+      result = [];
+
+  var isLarge = !isSorted && length >= largeArraySize && indexOf === Physics.util.indexOf,
+      seen = (callback || isLarge) ? getArray() : result;
+
+  if (isLarge) {
+    var cache = createCache(seen);
+    indexOf = cacheIndexOf;
+    seen = cache;
+  }
+  while (++index < length) {
+    var value = array[index],
+        computed = callback ? callback(value, index, array) : value;
+
+    if (isSorted ?
+          !index || seen[seen.length - 1] !== computed :
+          indexOf(seen, computed) < 0
+        ) {
+      if (callback || isLarge) {
+        seen.push(computed);
+      }
+      result.push(value);
+    }
+  }
+  if (isLarge) {
+    releaseArray(seen.array);
+    releaseObject(seen);
+  } else if (callback) {
+    releaseArray(seen);
+  }
+  return result;
+}
+
+/**
+ * Physics.util.uniq( array, [isSorted, callback] ) -> Array
+ * - array (Array): The array
+ * - isSorted (Boolean): Flag to indicate the array is sorted
+ * - callback (Function): Mapping function
+ *
+ * Create an array without duplicates.
+ **/
+Physics.util.uniq = function uniq(array, isSorted, callback) {
+  // juggle arguments
+  if (typeof isSorted !== 'boolean' && isSorted != null) {
+    callback = isSorted;
+    isSorted = false;
+  }
+  return baseUniq(array, isSorted, callback);
+};
+
+var assign = function(object, source, guard) {
+  var index, iterable = object, result = iterable;
+  if (!iterable) { return result; }
+  var args = arguments,
+      argsIndex = 0,
+      callback,
+      argsLength = typeof guard === 'number' ? 2 : args.length;
+  if (argsLength > 2 && typeof args[argsLength - 1] === 'function') {
+    callback = args[--argsLength];
+  }
+  while (++argsIndex < argsLength) {
+    iterable = args[argsIndex];
+    if (iterable && objectTypes[typeof iterable]) {
+        var ownIndex = -1,
+            ownProps = objectTypes[typeof iterable] && keys(iterable),
+            length = ownProps ? ownProps.length : 0;
+
+        while (++ownIndex < length) {
+          index = ownProps[ownIndex];
+          result[index] = callback ? callback(result[index], iterable[index]) : iterable[index];
+        }
+    }
+  }
+  return result;
+};
+
+/**
+ * Physics.util.extend( object, source...[, callback] ) -> Object
+ * - object (Object): The destination object
+ * - source (Object): The source objects
+ * - callback (Function): The function to customize assigning values
+ *
+ * Implementation of [lodash.extend](http://lodash.com/docs#assign)
+ **/
+Physics.util.extend = assign;
+
+/**
+ * Physics.util.defaults( object, source...[, callback] ) -> Object
+ * - object (Object): The destination object
+ * - source (Object): The source objects
+ * - callback (Function): The function to customize assigning values
+ *
+ * Implementation of [lodash.defaults](http://lodash.com/docs#defaults).
+ **/
+Physics.util.defaults = function(object, source, guard) {
+  var index, iterable = object, result = iterable;
+  if (!iterable){ return result; }
+  var args = arguments,
+      argsIndex = 0,
+      argsLength = typeof guard === 'number' ? 2 : args.length;
+  while (++argsIndex < argsLength) {
+    iterable = args[argsIndex];
+    if (iterable && objectTypes[typeof iterable]) {
+        var ownIndex = -1,
+            ownProps = objectTypes[typeof iterable] && keys(iterable),
+            length = ownProps ? ownProps.length : 0;
+
+        while (++ownIndex < length) {
+          index = ownProps[ownIndex];
+          if (typeof result[index] === 'undefined') {
+              result[index] = iterable[index];
+          }
+        }
+    }
+  }
+  return result;
+};
+
+/**
+ * Physics.util.sortedIndex( array, value[, callback] ) -> Number
+ * - array (Array): The array to inspect
+ * - value (Mixed): The value to evaluate
+ * - callback (Function): Function called per iteration
+ *
+ * Implementation of [lodash.sortedIndex](http://lodash.com/docs#sortedIndex).
+ **/
+Physics.util.sortedIndex = function sortedIndex(array, value, callback) {
+  var low = 0,
+      high = array ? array.length : low;
+
+  // explicitly reference `identity` for better inlining in Firefox
+  callback = callback || identity;
+  value = callback(value);
+
+  /* jshint -W030 */
+  while (low < high) {
+    var mid = (low + high) >>> 1;
+    (callback(array[mid]) < value) ?
+      low = mid + 1 :
+      high = mid;
+  }
+  /* jshint +W030 */
+  return low;
+};
+
+})();
+
+
+// ---
+// inside: src/util/scratchpad.js
+
+/*
+ * scratchpad
+ * thread-safe management of temporary (voletile)
+ * objects for use in calculations
+ * https://github.com/wellcaffeinated/scratchpad.js
+ */
+Physics.scratchpad = (function(){
+
+    // Errors
+    var SCRATCH_USAGE_ERROR = 'Error: Scratchpad used after .done() called. (Could it be unintentionally scoped?)';
+    var SCRATCH_INDEX_OUT_OF_BOUNDS = 'Error: Scratchpad usage space out of bounds. (Did you forget to call .done()?)';
+    var SCRATCH_MAX_REACHED = 'Error: Too many scratchpads created. (Did you forget to call .done()?)';
+    var ALREADY_DEFINED_ERROR = 'Error: Object is already registered.';
+
+    // cache previously created scratches
+    var scratches = [];
+    var numScratches = 0;
+    var Scratch, Scratchpad;
+
+    var regIndex = 0;
+
+
+    /** belongs to: Physics.scratchpad
+     * class Scratch
+     *
+     * A scratchpad session.
+     *
+     * This class keeps track of temporary objects used
+     * in this session and releases them when finished (call to `.done()`).
+     *
+     * Use this to retrieve temporary objects:
+     * - `.vector()`: retrieve a temporary [[Physics.vector]]
+     * - `.transform()`: retrieve a temporary [[Physics.transform]]
+     *
+     * See [[Physics.scratchpad]] for more info.
+     **/
+    Scratch = function Scratch(){
+
+        // private variables
+        this._active = false;
+        this._indexArr = [];
+
+        if (++numScratches >= Scratchpad.maxScratches){
+            throw SCRATCH_MAX_REACHED;
+        }
+    };
+
+    Scratch.prototype = {
+
+        /**
+         * Scratch#done( [val] ) -> Mixed
+         * - val (Mixed): No effect on this method, just passed on to the return value so you can do things like:
+         return scratch.done( myReturnVal );
+         * + (Mixed): Whatever you specified as `val`
+         *
+         * Declare that your work is finished.
+         *
+         * Release temp objects for use elsewhere. Must be called when immediate work is done.
+         *
+         * You can wrap the return value in scratch.done() so that you don't forget to call it.
+         *
+         * Example:
+         *
+         * ```javascript
+         * return scratch.done( myReturnValue );
+         * ```
+         **/
+        done: function( val ){
+
+            this._active = false;
+            var s;
+            for ( var i = 0; i < regIndex; ++i ){
+
+                this[ i ] = 0;
+            }
+
+            // add it back to the scratch stack for future use
+            scratches.push( this );
+            return val;
+        }
+    };
+
+
+    // API
+
+    /**
+     * Physics.scratchpad( [fn] ) -> Scratch|Function
+     * - fn (Function): Some function you'd like to wrap in a scratch session. First argument is the scratch instance.
+     * + (Function): The wrapped function (if `fn` arg specified) that can be reused like the original minus the first (scratch) parameter.
+     * + (Scratch): The scratch session.
+     *
+     * Get a new scratch session to work from or wrap a function in a scratch session.
+     *
+     * Call `.done()` on it when finished.
+     *
+     * Example:
+     *
+     * ```javascript
+     * // get a scratch session manually
+     * var myAlg = function( scratch, arg1, arg2, ... ){
+     *     var scratch = Physics.scratchpad()
+     *     ,vec = scratch.vector().set( 0, 0 ) // need to reinitialize... it's recycled!
+     *     ;
+     *     // ...
+     *     return scratch.done( result );
+     * };
+     * // later...
+     * while( awesome ){
+     *     myAlg( arg1, arg2, ... );
+     * }
+     * ```
+     *
+     * Example:
+     *
+     * ```javascript
+     * // wrap a function in a scratch session
+     * var myAlg = Physics.scratchpad(function( scratch, arg1, arg2, ... ){
+     *     var vec = scratch.vector().set( 0, 0 ); // need to reinitialize... it's recycled!
+     *     //...
+     *     return result;
+     * });
+     * // later...
+     * while( awesome ){
+     *     myAlg( arg1, arg2, ... );
+     * }
+     * ```
+     **/
+    Scratchpad = function Scratchpad( fn ){
+
+        if ( fn ){
+            return Scratchpad.fn( fn );
+        }
+
+        var scratch = scratches.pop() || new Scratch();
+        scratch._active = true;
+        return scratch;
+    };
+
+    // options
+    Scratchpad.maxScratches = 100; // maximum number of scratches
+    Scratchpad.maxIndex = 20; // maximum number of any type of temp objects
+
+    /**
+     * Physics.scratchpad.fn( fn ) -> Function
+     * - fn (Function): Some function you'd like to wrap in a scratch session. First argument is the scratch instance. See [[Physics.scratchpad]].
+     * + (Function): The wrapped function that can be reused like the original minus the first (scratch) parameter.
+     *
+     * Wrap a function in a scratch session.
+     *
+     * Same as calling `Physics.scratchpad( fn )` with a function specified.
+     **/
+    Scratchpad.fn = function( fn ){
+
+        var args = [];
+        for ( var i = 0, l = fn.length; i < l; i++ ){
+            args.push( i );
+        }
+
+        args = 'a' + args.join(',a');
+        /* jshint -W054 */
+        var handle = new Function('fn, scratches, Scratch', 'return function('+args+'){ '+
+               'var scratch = scratches.pop() || new Scratch( scratches );'+
+               'scratch._active = true;'+
+               'return scratch.done( fn(scratch, '+args+') );'+
+           '};'
+        );
+        /* jshint +W054 */
+
+        return handle(fn, scratches, Scratch);
+    };
+
+    /**
+     * Physics.scratchpad.register( name, constructor )
+     * - name (String): Name of the object class
+     * - constructor (Function): The object constructor
+     *
+     * Register a new object to be included in scratchpads.
+     *
+     * Example:
+     *
+     * ```javascript
+     * // register a hypothetical vector class...
+     * Physics.scratchpad.register('vector', Vector);
+     * ```
+     **/
+    Scratchpad.register = function register( name, constructor, options ){
+
+        var proto = Scratch.prototype
+            ,idx = regIndex++ // increase the scratch type index
+            ,stackname = '_' + name + 'Stack' // the name of the array stack
+            ,useFactory = options && options.useFactory
+            ;
+
+        if ( name in proto ) {
+            throw ALREADY_DEFINED_ERROR;
+        }
+
+        // create a new function on the prototype
+        Scratch.prototype[ name ] = function(){
+
+            // get the stack (or initialize it)
+            var stack = this[ stackname ] || (this[ stackname ] = [])
+                // we increase this index every time a voletile object is requested
+                // seems weird to store it on this as a number (ie: this.0, this.1)...
+                // but actually it's faster...
+                ,stackIndex = this[ idx ] | 0
+                ;
+
+            this[ idx ] = stackIndex + 1;
+
+            // if used after calling done...
+            if (!this._active){
+                throw SCRATCH_USAGE_ERROR;
+            }
+
+            // if too many objects created...
+            if (stackIndex >= Scratchpad.maxIndex){
+                throw SCRATCH_INDEX_OUT_OF_BOUNDS;
+            }
+
+            // return or create new instance
+            return stack[ stackIndex ] ||
+                    (stack[ stackIndex ] = useFactory ? constructor() : new constructor() );
+        };
+
+    };
+
+    // register some classes
+    Scratchpad.register('vector', Physics.vector);
+    Scratchpad.register('transform', Physics.transform);
+
+    return Scratchpad;
+
+})();
+
+
+// ---
+// inside: src/util/pubsub.js
+
+(function(){
+
+    var defaultPriority = 1;
+
+    function getPriority( val ){
+        return val._priority_;
+    }
+
+    // register a new scratch object so we can reuse event data
+    Physics.scratchpad.register('event', function(){ return {}; }, { useFactory: true });
+
+    /**
+     * class Physics.util.pubsub
+     *
+     * Fast pubsub implementation.
+     *
+     * Can be mixed into other classes easily.
+     **/
+    var PubSub = function PubSub(){
+
+        if (!(this instanceof PubSub)){
+            return new PubSub();
+        }
+    };
+
+    PubSub.prototype = {
+
+        /**
+         * Physics.util.pubsub#on( topic, fn( data, event )[, scope, priority] ) -> this
+         * Physics.util.pubsub#on( topicConfig[, scope, priority] ) -> this
+         * - topic (String): The topic name
+         * - topicConfig (Object): A config with key/value pairs of `{ topic: callbackFn, ... }`
+         * - fn (Function): The callback function (if not using Object as previous argument)
+         * - data (Mixed): The data sent from the call to `.emit()`
+         * - event (Object): Event data, holding `.topic`, the topic, and `.handler`, the `fn` callback.
+         * - scope (Object): The scope to bind callback to
+         * - priority (Number): The priority of the callback (higher is earlier)
+         *
+         * Subscribe callback(s) to a topic(s).
+         **/
+        on: function( topic, fn, scope, priority ){
+
+            var listeners
+                ,orig
+                ,idx
+                ;
+
+            // ensure topics hash is initialized
+            this._topics = this._topics || (this._topics = {});
+
+            // check if we're subscribing to multiple topics
+            // with an object
+            if ( Physics.util.isObject( topic ) ){
+
+                for ( var t in topic ){
+
+                    this.on( t, topic[ t ], fn, scope );
+                }
+
+                return this;
+            }
+
+            listeners = this._topics[ topic ] || (this._topics[ topic ] = []);
+            orig = fn;
+
+            if ( Physics.util.isObject( scope ) ){
+
+                fn = Physics.util.bind( fn, scope );
+                fn._bindfn_ = orig;
+                fn._one_ = orig._one_;
+                fn._scope_ = scope;
+
+            } else if ( priority === undefined ) {
+
+                priority = scope;
+            }
+
+            fn._priority_ = priority === undefined ? defaultPriority : priority;
+
+            idx = Physics.util.sortedIndex( listeners, fn, getPriority );
+
+            listeners.splice( idx, 0, fn );
+            return this;
+        },
+
+        /**
+         * Physics.util.pubsub#off( topic, fn[, scope] ) -> this
+         * Physics.util.pubsub#off( topicCfg ) -> this
+         * - topic (String): topic The topic name. Specify `true` to remove all listeners for all topics
+         * - topicCfg (Object): A config with key/value pairs of `{ topic: callbackFn, ... }`
+         * - fn (Function): The original callback function. Specify `true` to remove all listeners for specified topic
+         * - scope (Object): The scope the callback was bound to. This is important if you are binding methods that come from object prototypes.
+         *
+         * Unsubscribe callback(s) from topic(s).
+         **/
+        off: function( topic, fn, scope ){
+
+            var listeners
+                ,listn
+                ;
+
+            if ( !this._topics ){
+                // nothing subscribed
+                return this;
+            }
+
+            if ( topic === true ){
+                // purge all listeners
+                this._topics = {};
+                return this;
+            }
+
+            // check if we're subscribing to multiple topics
+            // with an object
+            if ( Physics.util.isObject( topic ) ){
+
+                for ( var t in topic ){
+
+                    this.off( t, topic[ t ] );
+                }
+
+                return this;
+            }
+
+            listeners = this._topics[ topic ];
+
+            if (!listeners){
+                return this;
+            }
+
+            if ( fn === true ){
+                // purge all listeners for topic
+                this._topics[ topic ] = [];
+                return this;
+            }
+
+            for ( var i = 0, l = listeners.length; i < l; i++ ){
+
+                listn = listeners[ i ];
+
+                if (
+                    (listn._bindfn_ === fn || listn === fn) &&
+                    ( (!scope) || listn._scope_ === scope) // check the scope too if specified
+                ){
+                    listeners.splice( i, 1 );
+                    break;
+                }
+            }
+
+            return this;
+        },
+
+        /**
+         * Physics.util.pubsub#emit( topic[, data] ) -> this
+         * - topic (String): The topic name
+         * - data (Mixed): The data to send
+         *
+         * Publish data to a topic.
+         **/
+        emit: function( topic, data ){
+
+            if ( !this._topics ){
+                // nothing subscribed
+                return this;
+            }
+
+            var listeners = this._topics[ topic ]
+                ,l = listeners && listeners.length
+                ,handler
+                ,e
+                ,scratch = Physics.scratchpad()
+                ;
+
+            if ( !l ){
+                return scratch.done(this);
+            }
+
+            e = scratch.event();
+            // event data
+            e.topic = topic;
+            e.handler = handler;
+
+            // reverse iterate so priorities work out correctly
+            while ( l-- ){
+
+                handler = listeners[ l ];
+                handler( data, e );
+
+                // if _one_ flag is set, the unsubscribe
+                if ( handler._one_ ){
+                    listeners.splice( l, 1 );
+                }
+            }
+
+            return scratch.done(this);
+        },
+
+        /**
+         * Physics.util.pubsub#one( topic, fn( data, event )[, scope, priority] ) -> this
+         * Physics.util.pubsub#one( topicConfig[, scope, priority] ) -> this
+         * - topic (String): The topic name
+         * - topicConfig (Object): A config with key/value pairs of `{ topic: callbackFn, ... }`
+         * - fn (Function): The callback function (if not using Object as previous argument)
+         * - data (Mixed): The data sent from the call to `.emit()`
+         * - event (Object): Event data, holding `.topic`, the topic, and `.handler`, the `fn` callback.
+         * - scope (Object): The scope to bind callback to
+         * - priority (Number): The priority of the callback (higher is earlier)
+         *
+         * Subscribe callback(s) to a topic(s), but only ONCE.
+         **/
+        one: function( topic, fn, scope ){
+
+            // check if we're subscribing to multiple topics
+            // with an object
+            if ( Physics.util.isObject( topic ) ){
+
+                for ( var t in topic ){
+
+                    this.one( t, topic[ t ], fn, scope );
+                }
+
+                return this;
+            }
+
+            // set the _one_ flag
+            fn._one_ = true;
+            this.on( topic, fn, scope );
+
+            return this;
+        }
+    };
+
+    Physics.util.pubsub = PubSub;
+})();
+
+
+// ---
+// inside: src/util/ticker.js
+
+/**
+ * class Physics.util.ticker
+ *
+ * The Ticker _singleton_ for easily binding callbacks to animation loops (requestAnimationFrame).
+ *
+ * Requires window.requestAnimationFrame... so polyfill it if you need to.
+ **/
+(function(window){
+
+    var active = true
+        ,ps = Physics.util.pubsub()
+        ,perf = window.performance
+        ;
+
+    function now(){
+        // http://updates.html5rocks.com/2012/05/requestAnimationFrame-API-now-with-sub-millisecond-precision
+        return (perf && perf.now) ?
+            (perf.now() + perf.timing.navigationStart) :
+            Date.now();
+    }
+
+    /*
+     * step( time )
+     * - time (Number): The current time
+     *
+     * Publish a tick to subscribed callbacks
+     */
+    function step(){
+
+        var time;
+
+        window.requestAnimationFrame( step );
+
+        if (!active){
+            return;
+        }
+
+        time = now();
+
+        if (!time){
+            return;
+        }
+
+        ps.emit( 'tick', time );
+    }
+
+    // start stepping if we can
+    if ( window.requestAnimationFrame ){
+        step();
+    } else {
+        active = false;
+    }
+
+    /**
+     * Physics.util.ticker.start() -> this
+     *
+     * Start the ticker
+     **/
+    function start(){
+
+        active = true;
+        return this;
+    }
+
+    /**
+     * Physics.util.ticker.stop() -> this
+     *
+     * Stop the ticker
+     **/
+    function stop(){
+
+        active = false;
+        return this;
+    }
+
+    /**
+     * Physics.util.ticker.on( listener( time ) ) -> this
+     * - listener (Function): The callback function
+     * - time (Number): The current timestamp
+     *
+     * Subscribe a callback to the ticker.
+     **/
+    function on( listener ){
+
+        ps.on('tick', listener);
+        return this;
+    }
+
+    /**
+     * Physics.util.ticker.off( listener ) -> this
+     * - listener (Function): The callback function previously bound
+     *
+     * Unsubscribe a callback from the ticker.
+     **/
+    function off( listener ){
+
+        ps.off('tick', listener);
+        return this;
+    }
+
+    /**
+     * Physics.util.ticker.isActive() -> Boolean
+     * + (Boolean): `true` if running, `false` otherwise.
+     *
+     * Determine if ticker is currently running.
+     **/
+    function isActive(){
+
+        return !!active;
+    }
+
+    // API
+    Physics.util.ticker = {
+        now: now,
+        start: start,
+        stop: stop,
+        on: on,
+        off: off,
+        isActive: isActive
+    };
+
+}(this));
+
+
+// ---
+// inside: src/core/query.js
+
+(function (window) {
+
+    /*
+     * Group helpers
+     */
+    var fnTrue = function(){ return !0; }; // return true
+    
+    var indexOf = Physics.util.indexOf;
+
+    /** hide
+     * wrapRule( fn( propVal ), prop ) -> Function
+     * - fn (Function): The test function
+     * - prop (String): The property name to test
+     * - propVal (Mixed): The property value
+     * 
+     * Get test function to test on sub property.
+     **/
+    var wrapRule = function wrapRule( fn, prop ){
+        return function( thing ){
+            return fn( thing[ prop ] );
+        };
+    };
+
+    /** hide
+     * $eq( toMatch[, prop] ) -> Function
+     * - toMatch (Mixed): The value to match
+     * - prop (String): The property name to test
+     * 
+     * Get an equality test function.
+     **/
+    var $eq = function $eq( toMatch, prop ){
+        return function( thing ){
+            
+            thing = prop ? thing[ prop ] : thing;
+
+            var fr = 0
+                ,bk
+                ;
+            
+            if ( Physics.util.isArray( thing ) ){
+
+                if ( Physics.util.isArray( toMatch ) ){
+                    // match all
+                    bk = thing.length;
+
+                    // check lengths
+                    if ( bk !== toMatch.length ){
+                        return false;
+                    }
+
+                    while ( fr < bk ){
+                        bk--;
+                        if (
+                            // check front
+                            (indexOf(toMatch, thing[ fr ]) === -1) ||
+                            // check back
+                            (indexOf(toMatch, thing[ bk ]) === -1)
+                        ) {
+                            return false;
+                        }
+                        fr++;
+                    }
+                    return true;
+                } else {
+                    // find in array
+                    return (indexOf( thing, toMatch ) > -1);
+                }
+            }
+
+            // exact match
+            return (thing === toMatch);
+        };
+    };
+
+    /** hide
+     * $ne( toMatch[, prop] ) -> Function
+     * - toMatch (Mixed): The value to match
+     * - prop (String): The property name to test
+     * 
+     * Get a NOT equality test function.
+     **/
+    var $ne = function $ne( toMatch, prop ){
+        var fn = $eq( toMatch, prop );
+        return function( thing ){
+            return !fn( thing );
+        };
+    };
+
+    /** hide
+     * $in( toMatch[, prop] ) -> Function
+     * - toMatch (Array): The array to match
+     * - prop (String): The property name to test
+     * 
+     * Get a test function for matching ANY in array
+     **/
+    var $in = function $in( toMatch, prop ){
+        return function( thing ){
+
+            thing = prop ? thing[ prop ] : thing;
+            
+            var fr = 0
+                ,bk
+                ;
+
+            if ( Physics.util.isArray( thing ) ){
+                bk = thing.length;
+
+                while( fr < bk ){
+                    bk--;
+                    if (
+                        // check front
+                        (indexOf(toMatch, thing[ fr ]) > -1) ||
+                        // check back
+                        (indexOf(toMatch, thing[ bk ]) > -1)
+                    ) {
+                        return true;
+                    }
+                    fr++;
+                }
+                return false;
+            }
+
+            // if thing matches any in array
+            return (indexOf(toMatch, thing) > -1);
+        };
+    };
+
+    /** hide
+     * $nin( toMatch[, prop] ) -> Function
+     * - toMatch (Array): The array to match
+     * - prop (String): The property name to test
+     * 
+     * Get a test function for matching NOT ANY in array
+     **/
+    var $nin = function $nin( toMatch, prop ){
+        var fn = $in( toMatch, prop );
+        return function( thing ){
+            return !fn( thing );
+        };
+    };
+
+    /** hide
+     * $at( point ) -> Function
+     * - point (Vectorish): The point to check
+     * 
+     * Get a test function to match any body who's aabb intersects point
+     **/
+    var $at = function $at( point ){
+        point = new Physics.vector( point );
+        return function( body ){
+            var aabb = body.aabb();
+            return Physics.aabb.contains( aabb, point );
+        };
+    };
+
+    /** hide
+     * $and( first ) -> Function
+     * - first (Function): First function node. `first.next` should have the next function, and so on.
+     * 
+     * Get an AND test function.
+     **/
+    var $and = function $and( first ){
+        return first.next ? function( thing ){
+            var fn = first;
+            while ( fn ){
+
+                if ( !fn( thing ) ){
+                    return false;
+                }
+                fn = fn.next;
+            }
+            return true;
+        } : first;
+    };
+
+    /** hide
+     * $or( first ) -> Function
+     * - first (Function): First function node. `first.next` should have the next function, and so on.
+     * 
+     * Get an OR test function.
+     **/
+    var $or = function $or( first ){
+        return first.next ? function( thing ){
+            var fn = first;
+            while ( fn ){
+
+                if ( fn( thing ) ){
+                    return true;
+                }
+                fn = fn.next;
+            }
+            return false;
+        } : first;
+    };
+
+    // operation hash
+    var operations = {
+        // $and and $or are separate
+        $eq: $eq
+        ,$ne: $ne
+        ,$in: $in
+        ,$nin: $nin
+        ,$at: $at
+    };
+
+    /** related to: Physics.world#find
+     * Physics.query( rules ) -> Function
+     * - rules (Object): The mongodb-like search rules. (See description).
+     * + (Function): The test function
+     * 
+     * Creates a function that can be used to perform tests on objects.
+     *
+     * The test function will return a [[Boolean]]; `true` if the object matches the tests.
+     *
+     * Query rules are mongodb-like. You can specify a hash of values to match like this:
+     *
+     * ```javascript
+     * {
+     *     foo: 'bar',
+     *     baz: 2,
+     *     some: {
+     *         nested: 'value'
+     *     }
+     * }
+     * ```
+     *
+     * And they will all need to match (it's an AND rule).
+     *
+     * You can also use operators for more versatility. The operators you can use include:
+     *
+     * - $eq: Test if some property is equal to a value (this is done by default, and is thus redundant)
+     * - $ne: Test if some property is _NOT_ equal to a value
+     * - $in: Test if some value (or array of values) is one of the specified array of values
+     * - $nin: Test if some value (or array of values) is _NOT_ one of the specified array of values
+     * - $at: Test if a body's [[Physics.aabb]] includes specified point. It's a primative hit-test.
+     * 
+     * Example:
+     *
+     * ```javascript
+     * var wheelsArray = [];
+     * 
+     * var queryFn = Physics.query({
+     *     name: 'circle', // only circles
+     *     $nin: wheelsArray, // not in the wheelsArray
+     *     labels: { $in: [ 'player', 'monster' ] } // that have player OR monster labels
+     * });
+     *
+     * var obj = {
+     *     name: 'circle',
+     *     labels: [ 'round' ]
+     * };
+     *
+     * queryFn( obj ); // -> false
+     * // give it a player tag
+     * obj.labels.push('player');
+     * queryFn( obj ); // -> true
+     * // put it inside the wheelsArray
+     * wheelsArray.push( obj );
+     * queryFn( obj ); // -> false
+     * ```
+     **/
+    var Query = function Query( rules, /* internal use */ $op ){
+
+        var op
+            ,l
+            ,rule
+            ,first
+            ,list
+            ,fn
+            ;
+
+        if ( $op ){
+            
+            // parse operation choice
+            if ( $op === '$or' || $op === '$and' ){
+
+                // expect a rules array
+                for ( op = 0, l = rules.length; op < l; ++op ){
+                    
+                    fn = Query( rules[ op ] );
+                    // if first hasn't been set yet, set it and start the list there
+                    // otherwise set the next node of the list
+                    list = list ? list.next = fn : first = fn;
+                }
+
+                return ($op === '$or') ? $or( first ) : $and( first );
+            } else if ( op = operations[ $op ] ){
+
+                return op( rules );
+
+            } else {
+                // does not compute...
+                throw 'Unknown query operation: ' + $op;
+            }
+        }
+
+        // loop through rules
+        for ( op in rules ){
+            rule = rules[ op ];
+   
+            if ( op[0] === '$' ){
+                // it's an operation rule
+                fn = Query( rule, op );
+                
+            } else if ( Physics.util.isPlainObject( rule ) ) {
+                // it's an object so parse subrules
+                fn = wrapRule( Query( rule ), op );
+            } else {
+                // simple equality rule
+                fn = $eq( rule, op );
+            }
+
+            // if first hasn't been set yet, set it and start the list there
+            // otherwise set the next node of the list
+            list = list ? list.next = fn : first = fn;
+        }
+
+        // return the rules test
+        return $and( first || fnTrue );
+    };
+
+    Physics.query = Query;
+
+})(this);
+
+
+// ---
+// inside: src/core/behavior.js
+
+(function(){
+
+    var defaults = {
+        priority: 0
+    };
+
+    /** related to: Physics.util.decorator
+     * Physics.behavior( name[, options] ) -> Behavior
+     * - name (String): The name of the behavior to create
+     * - options (Object): The configuration for that behavior ( depends on behavior ).
+       Available options and defaults:
+       
+       ```javascript
+        {
+           priority: 0 // the priority of this body
+        }
+       ```
+     *
+     * Factory function for creating Behaviors.
+     *
+     * Visit [the PhysicsJS wiki on Behaviors](https://github.com/wellcaffeinated/PhysicsJS/wiki/Behaviors)
+     * for usage documentation.
+     **/
+    Physics.behavior = Decorator('behavior', {
+
+        /** belongs to: Physics.behavior
+         * class Behavior
+         *
+         * The base class for behaviors created by [[Physics.behavior]] factory function.
+         **/
+
+        /** internal
+         * Behavior#init( options )
+         * - options (Object): The configuration options passed by the factory
+         * 
+         * Initialization. Internal use.
+         **/
+        init: function( options ){
+            
+            /** related to: Physics.util.options
+             * Behavior#options( options ) -> Object
+             * - options (Object): The options to set as an object
+             * + (Object): The options
+             * 
+             * Set options on this instance. 
+             * 
+             * Access options directly from the options object.
+             * 
+             * Example:
+             *
+             * ```javascript
+             * this.options.someOption;
+             * ```
+             * 
+             **/
+            this.options = Physics.util.options( defaults );
+            this.options( options );
+        },
+
+        /**
+         * Behavior#applyTo( arr ) -> this
+         * - arr (Array): Array of bodies to apply this behavior to. Specify `true` for all objects in world.
+         * 
+         * Apply the behavior to a group of bodies.
+         **/
+        applyTo: function( arr ){
+
+            if ( arr === true ){
+                this._targets = null;
+            } else {
+                this._targets = Physics.util.uniq( arr );
+            }
+            return this;
+        },
+
+        /**
+         * Behavior#getTargets() -> Array
+         * + (Array): The array of bodies (by reference!) this behavior is applied to.
+         * 
+         * Get the array of bodies (by reference!) this behavior is applied to.
+         **/
+        getTargets: function(){
+            
+            return this._targets || ( this._world ? this._world._bodies : [] );
+        },
+
+        /**
+         * Behavior#setWorld( world ) -> this
+         * - world (Object): The world (or null)
+         *
+         * Set which world to apply to.
+         *
+         * Usually this is called internally. Shouldn't be a need to call this yourself usually.
+         **/
+        setWorld: function( world ){
+
+            if ( this.disconnect && this._world ){
+                this.disconnect( this._world );
+            }
+
+            this._world = world;
+
+            if ( this.connect && world ){
+                this.connect( world );
+            }
+
+            return this;
+        },
+
+        /**
+         * Behavior#connect( world )
+         * - world (Physics.world): The world to connect to
+         * 
+         * Connect to a world.
+         *
+         * Extend this when creating behaviors if you need to specify pubsub management.
+         * Automatically called when added to world by the [[Behavior#setWorld]] method.
+         **/
+        connect: function( world ){
+
+            if (this.behave){
+                world.on('integrate:positions', this.behave, this, this.options.priority);
+            }
+        },
+
+        /**
+         * Behavior#disconnect( world )
+         * - world (Physics.world): The world to disconnect from
+         * 
+         * Disconnect from a world.
+         *
+         * Extend this when creating behaviors if you need to specify pubsub management.
+         * Automatically called when added to world by the [[Behavior#setWorld]] method.
+         **/
+        disconnect: function( world ){
+
+            if (this.behave){
+                world.off('integrate:positions', this.behave, this);
+            }
+        },
+
+        /**
+         * Behavior#behave( data )
+         * - data (Object): The pubsub `integrate:positions` event data
+         * 
+         * Default method run on every world integration.
+         *
+         * You _must_ extend this when creating a behavior,
+         * unless you extend the [[Behavior#connect]] and [[Behavior#disconnect]] methods.
+         **/
+        behave: null
+    });
+
+}());
+
+// ---
+// inside: src/core/body.js
+
+(function(){
+
+    var defaults = {
+
+        // is the body hidden (not to be rendered)?
+        hidden: false,
+        // is the body `dynamic`, `kinematic` or `static`?
+        // http://www.box2d.org/manual.html#_Toc258082973
+        treatment: 'dynamic',
+        // body mass
+        mass: 1.0,
+        // body restitution. How "bouncy" is it?
+        restitution: 1.0,
+        // what is its coefficient of friction with another surface with COF = 1?
+        cof: 0.8,
+        // what is the view object (mixed) that should be used when rendering?
+        view: null
+    };
+
+    var uidGen = 1;
+
+    var Pi2 = Math.PI * 2;
+    function cycleAngle( ang ){
+        return ((ang % Pi2) + Pi2) % Pi2;
+    }
+
+    /** related to: Physics.util.decorator
+     * Physics.body( name[, options] ) -> Body
+     * - name (String): The name of the body to create
+     * - options (Object): The configuration for that body ( depends on body ).
+       Available options and defaults:
+
+       ```javascript
+        {
+            // is the body hidden (not to be rendered)?
+            hidden: false,
+            // is the body `dynamic`, `kinematic` or `static`?
+            // http://www.box2d.org/manual.html#_Toc258082973
+            treatment: 'dynamic',
+            // body mass
+            mass: 1.0,
+            // body restitution. How "bouncy" is it?
+            restitution: 1.0,
+            // what is its coefficient of friction with another surface with COF = 1?
+            cof: 0.8,
+            // what is the view object (mixed) that should be used when rendering?
+            view: null,
+            // the vector offsetting the geometry from its center of mass
+            offset: Physics.vector(0,0)
+        }
+       ```
+     *
+     * Factory function for creating Bodies.
+     *
+     * Visit [the PhysicsJS wiki on Bodies](https://github.com/wellcaffeinated/PhysicsJS/wiki/Bodies)
+     * for usage documentation.
+     **/
+    Physics.body = Decorator('body', {
+
+        /** belongs to: Physics.body
+         * class Body
+         *
+         * The base class for bodies created by [[Physics.body]] factory function.
+         **/
+
+        /** internal
+         * Body#init( options )
+         * - options (Object): The configuration options passed by the factory
+         *
+         * Initialization. Internal use.
+         **/
+        init: function( options ){
+
+            var self = this;
+            var vector = Physics.vector;
+
+            /** related to: Physics.util.options
+             * Body#options( options ) -> Object
+             * - options (Object): The options to set as an object
+             * + (Object): The options
+             *
+             * Set options on this instance.
+             *
+             * Access options directly from the options object.
+             *
+             * Example:
+             *
+             * ```javascript
+             * this.options.someOption;
+             * ```
+             *
+             **/
+            // all options get copied onto the body.
+            this.options = Physics.util.options( defaults, this );
+            this.options.onChange(function( opts ){
+                self.offset = new vector( opts.offset );
+            });
+            this.options( options );
+
+            /**
+             * Body#state
+             *
+             * The physical state container.
+             *
+             * - ``this.state.pos`` ([[Physics.vector]]) The position vector.
+             * - ``this.state.vel`` ([[Physics.vector]]) The velocity vector.
+             * - ``this.state.acc`` ([[Physics.vector]]) The acceleration vector.
+             * - ``this.state.angular.pos`` ([[Number]]) The angular position (in radians, positive is clockwise starting along the x axis)
+             * - ``this.state.angular.vel`` ([[Number]]) The angular velocity
+             * - ``this.state.angular.acc`` ([[Number]]) The angular acceleration
+             *
+             * Properties from the previous timestep are stored in:
+             * ```javascript
+             * this.state.old; // .pos, .vel, ...
+             * ```
+             **/
+            this.state = {
+                pos: new vector( this.x, this.y ),
+                vel: new vector( this.vx, this.vy ),
+                acc: new vector(),
+                angular: {
+                    pos: this.angle || 0.0,
+                    vel: this.angularVelocity || 0.0,
+                    acc: 0.0
+                },
+                old: {
+                    pos: new vector(),
+                    vel: new vector(),
+                    acc: new vector(),
+                    angular: {
+                        pos: 0.0,
+                        vel: 0.0,
+                        acc: 0.0
+                    }
+                }
+            };
+
+            // private storage for sleeping
+            this._sleepAngPosMean = 0;
+            this._sleepAngPosVariance = 0;
+            this._sleepPosMean = new vector();
+            this._sleepPosVariance = new vector();
+            this._sleepMeanK = 0;
+
+            // cleanup
+            delete this.x;
+            delete this.y;
+            delete this.vx;
+            delete this.vy;
+            delete this.angle;
+            delete this.angularVelocity;
+
+            if (this.mass === 0){
+                throw "Error: Bodies must have non-zero mass";
+            }
+
+            /**
+             * Body#uid = Number
+             *
+             * The unique id for the body
+             **/
+            this.uid = uidGen++;
+
+            /** related to: Physics.geometry
+             * Body#geometry
+             *
+             * The geometry for this body.
+             *
+             * By default it is a `point` geometry which gets overridden.
+             **/
+            this.geometry = Physics.geometry('point');
+
+            /**
+             * Body#mass = 1.0
+             *
+             * The mass.
+             **/
+
+            /**
+             * Body#offset
+             *
+             * The vector offsetting the body's shape from its center of mass.
+             **/
+
+             /**
+              * Body#restitution = 1.0
+              *
+              * The restitution.
+              *
+              * This is the "bounciness" of the body.
+              * It's a number between `0` and `1`.
+              *
+              * A restitution of 1 is the bounciest.
+              *
+              * A restitution of 0 is not bouncy.
+              *
+              * When colliding the restitutions of bodies are
+              * multiplied together to get the restitution between two
+              * bodies.
+              *
+              **/
+
+              /**
+               * Body#cof = 0.8
+               *
+               * The coefficient of friction of the body.
+               *
+               * It's how much "slide" it has during collisions.
+               *
+               * A `cof` of `0` will really slidy.
+               *
+               * A `cof` of `1` has no slide.
+               *
+               * This is a very simplistic implementation at the moment.
+               * What would be better is to have both static and kinetic
+               * friction. But that's not done yet.
+               **/
+
+               /**
+                * Body#treatment = String
+                *
+                * How the body is treated by the simulation.
+                *
+                * The body can be `dynamic`, `kinematic` or `static` as
+                * described by the [analogous box2d docs](http://www.box2d.org/manual.html#_Toc258082973).
+                *
+                * * _dynamic_ bodies are treated "normally". They are integrated, and collide, and all that.
+                * * _kinematic_ bodies are bodies that move at a specified velocity. Other bodies collide with them, but they don't bounce off of other bodies.
+                * * _static_ bodies just stand still. They are like obstacles.
+                **/
+
+                /**
+                 * Body#hidden = false
+                 *
+                 * Determines whether the body should be hidden by the renderer.
+                 **/
+
+                /** related to: Physics.renderer
+                 * Body#view = it_depends
+                 *
+                 * Storage for use by the renderer.
+                 *
+                 * The type of renderer will put different things in the view property.
+                 * Basically, this is how the body "looks". It could be a HTMLElement, or
+                 * an Image, etc...
+                 *
+                 * If your body changes appearance (shape), you should modify this somehow
+                 * otherwise the renderer will keep using this same view. If you're letting
+                 * the renderer create the view for you, just set this to `undefined` if the
+                 * body gets modified in shape during the simulation.
+                 **/
+
+                /** related to: Physics.renderer
+                 * Body#styles
+                 *
+                 * The styles the renderer should use for creating the view.
+                 *
+                 * The styles depend on the renderer. See [[Renderer#createView]] for style options.
+                 **/
+        },
+
+        /**
+         * Body#sleep( [dt] ) -> Boolean
+         * - dt (Number): Time to advance the idle time
+         * - dt (Boolean): If `true`, the body will be forced to sleep. If `false`, the body will be forced to awake.
+         *
+         * Get and/or set whether the body is asleep.
+         *
+         * If called with a time (in ms), the time will be added to the idle time and sleep conditions will be checked.
+         **/
+        sleep: function( dt ){
+
+            if ( dt === true ){
+                // force sleep
+                this.asleep = true;
+
+            } else if ( dt === false ){
+                // force wakup
+                this.asleep = false;
+                this._sleepMeanK = 0;
+                this._sleepAngPosMean = 0;
+                this._sleepAngPosVariance = 0;
+                this._sleepPosMean.zero();
+                this._sleepPosVariance.zero();
+                this.sleepIdleTime = 0;
+
+            } else if ( dt && !this.asleep ) {
+
+                this.sleepCheck( dt );
+            }
+
+            return this.asleep;
+        },
+
+        /**
+         * Body#sleepCheck( [dt] )
+         * - dt (Number): Time to advance the idle time
+         *
+         * Check if the body should be sleeping.
+         *
+         * Call with no arguments if some event could possibly wake up the body. This will force the body to recheck.
+         **/
+        sleepCheck: function( dt ){
+
+            var opts = this._world && this._world.options;
+
+            // if sleeping disabled. stop.
+            if ( this.sleepDisabled || (opts && opts.sleepDisabled) ){
+                return;
+            }
+
+            var limit
+                ,v
+                ,d
+                ,r
+                ,aabb
+                ,scratch = Physics.scratchpad()
+                ,diff = scratch.vector()
+                ,diff2 = scratch.vector()
+                ,kfac
+                ,stats
+                ;
+
+            dt = dt || 0;
+            aabb = this.geometry.aabb();
+            r = Math.max(aabb.hw, aabb.hh);
+
+            if ( this.asleep ){
+                // check velocity
+                v = this.state.vel.norm() + Math.abs(r * this.state.angular.vel);
+                limit = this.sleepSpeedLimit || (opts && opts.sleepSpeedLimit) || 0;
+
+                if ( v >= limit ){
+                    this.sleep( false );
+                    return scratch.done();
+                }
+            }
+
+            this._sleepMeanK++;
+            kfac = this._sleepMeanK > 1 ? 1/(this._sleepMeanK - 1) : 0;
+            Physics.statistics.pushRunningVectorAvg( this.state.pos, this._sleepMeanK, this._sleepPosMean, this._sleepPosVariance );
+            // we take the sin because that maps the discontinuous angle to a continuous value
+            // then the statistics calculations work better
+            stats = Physics.statistics.pushRunningAvg( Math.sin(this.state.angular.pos), this._sleepMeanK, this._sleepAngPosMean, this._sleepAngPosVariance );
+            this._sleepAngPosMean = stats[0];
+            this._sleepAngPosVariance = stats[1];
+            v = this._sleepPosVariance.norm() + Math.abs(r * Math.asin(stats[1]));
+            v *= kfac;
+            limit = this.sleepVarianceLimit || (opts && opts.sleepVarianceLimit) || 0;
+            // console.log(v, limit, kfac, this._sleepPosVariance.norm(), stats[1])
+            if ( v <= limit ){
+                // check idle time
+                limit = this.sleepTimeLimit || (opts && opts.sleepTimeLimit) || 0;
+                this.sleepIdleTime = (this.sleepIdleTime || 0) + dt;
+
+                if ( this.sleepIdleTime > limit ){
+                    this.asleep = true;
+                }
+            } else {
+                this.sleep( false );
+            }
+
+            scratch.done();
+        },
+
+        /**
+         * Body#setWorld( world ) -> this
+         * - world (Object): The world (or null)
+         *
+         * Set which world to apply to.
+         *
+         * Usually this is called internally. Shouldn't be a need to call this yourself usually.
+         **/
+        setWorld: function( world ){
+
+            if ( this.disconnect && this._world ){
+                this.disconnect( this._world );
+            }
+
+            this._world = world;
+
+            if ( this.connect && world ){
+                this.connect( world );
+            }
+
+            return this;
+        },
+
+        /**
+         * Body#accelerate( acc ) -> this
+         * - acc (Physics.vector): The acceleration vector
+         *
+         * Accelerate the body by adding supplied vector to its current acceleration
+         **/
+        accelerate: function( acc ){
+
+            if ( this.treatment === 'dynamic' ){
+                this.state.acc.vadd( acc );
+            }
+
+            return this;
+        },
+
+        /**
+         * Body#applyForce( force[, p] ) -> this
+         * - force (Vectorish): The force vector
+         * - p (Vectorish): The point vector from the COM at which to apply the force
+         *
+         * Apply a force at center of mass, or at point `p` relative to the center of mass
+         **/
+        applyForce: function( force, p ){
+
+            if ( this.treatment !== 'dynamic' ){
+                return this;
+            }
+
+            var scratch = Physics.scratchpad()
+                ,r = scratch.vector()
+                ,state
+                ;
+
+            // if no point at which to apply the force... apply at center of mass
+            if ( p && this.moi ){
+
+                // apply torques
+                state = this.state;
+                r.clone( p );
+                // r cross F
+                this.state.angular.acc -= r.cross( force ) / this.moi;
+            }
+
+            this.accelerate( r.clone( force ).mult( 1/this.mass ) );
+
+            scratch.done();
+            return this;
+        },
+
+        /** related to: Body#offset
+         * Body#getGlobalOffset( [out] ) -> Physics.vector
+         * - out (Physics.vector): A vector to use to put the result into. One is created if `out` isn't specified.
+         * + (Physics.vector): The offset in global coordinates
+         *
+         * Get the body offset vector (from the center of mass) for the body's shape in global coordinates.
+         **/
+        getGlobalOffset: function( out ){
+
+            out = out || new Physics.vector();
+            out.clone( this.offset ).rotate( this.state.angular.pos );
+            return out;
+        },
+
+        /** related to: Physics.aabb
+         * Body#aabb() -> Object
+         * + (Object): The aabb of this body
+         *
+         * Get the Axis aligned bounding box for the body in its current position and rotation
+         **/
+        aabb: function(){
+
+            var angle = this.state.angular.pos
+                ,scratch = Physics.scratchpad()
+                ,v = scratch.vector()
+                ,aabb = this.geometry.aabb( angle )
+                ;
+
+            this.getGlobalOffset( v );
+
+            aabb.x += this.state.pos._[0] + v._[0];
+            aabb.y += this.state.pos._[1] + v._[1];
+
+            return scratch.done( aabb );
+        },
+
+        /**
+         * Body#toBodyCoords( v ) -> Physics.vector
+         * - v (Physics.vector): The vector to transform
+         * + (Physics.vector): The transformed vector
+         *
+         * Transform a vector into coordinates relative to this body.
+         **/
+        toBodyCoords: function( v ){
+            return v.vsub( this.state.pos ).rotate( -this.state.angular.pos );
+        },
+
+        /**
+          * Body#toWorldCoords( v ) -> Physics.vector
+          * - v (Physics.vector): The vector to transform
+          * + (Physics.vector): The transformed vector
+          *
+          * Transform a vector from body coordinates into world coordinates.
+          **/
+        toWorldCoords: function( v ){
+            return v.rotate( this.state.angular.pos ).vadd( this.state.pos );
+        },
+
+        /**
+         * Body#recalc() -> this
+         *
+         * Recalculate properties.
+         *
+         * Intended to be overridden by subclasses. Call when body physical properties are changed.
+         **/
+        recalc: function(){
+            // override to recalculate properties
+            return this;
+        }
+    });
+
+    /**
+     * Body.getCOM( bodies[, com] ) -> Physics.vector
+     * - bodies (Array): The list of bodies
+     * - com (Physics.vector): The vector to put result into. A new vector will be created if not provided.
+     * + (Physics.vector): The center of mass position
+     *
+     * Get center of mass position from list of bodies.
+     **/
+    Physics.body.getCOM = function( bodies, com ){
+        // @TODO add a test for this fn
+        var b
+            ,pos
+            ,i
+            ,l = bodies && bodies.length
+            ,M = 0
+            ;
+
+        com = com || new Physics.vector();
+
+        if ( !l ){
+            return com.zero();
+        }
+
+        if ( l === 1 ){
+            return com.clone( bodies[0].state.pos );
+        }
+
+        com.zero();
+
+        for ( i = 0; i < l; i++ ){
+            b = bodies[ i ];
+            pos = b.state.pos;
+            com.add( pos._[0] * b.mass, pos._[1] * b.mass );
+            M += b.mass;
+        }
+
+        com.mult( 1 / M );
+
+        return com;
+    };
+
+}());
+
+
+// ---
+// inside: src/core/geometry.js
+
+(function(){
+    /** related to: Physics.util.decorator
+     * Physics.geometry( name[, options] ) -> Geometry
+     * - name (String): The name of the geometry to create
+     * - options (Object): The configuration for that geometry ( depends on geometry ).
+     *
+     * Factory function for creating Geometries.
+     *
+     * Visit [the PhysicsJS wiki on Geometries](https://github.com/wellcaffeinated/PhysicsJS/wiki/Geometries)
+     * for usage documentation.
+     **/
+    Physics.geometry = Decorator('geometry', {
+
+        /** belongs to: Physics.geometry
+         * class Geometry
+         *
+         * The base class for geometries created by [[Physics.geometry]] factory function.
+         **/
+
+        /** internal
+         * Geometry#init( options )
+         * - options (Object): The configuration options passed by the factory
+         * 
+         * Initialization. Internal use.
+         **/
+        init: function( options ){
+
+            /** related to: Physics.util.options
+             * Geometry#options( options ) -> Object
+             * - options (Object): The options to set as an object
+             * + (Object): The options
+             * 
+             * Set options on this instance. 
+             * 
+             * Access options directly from the options object.
+             * 
+             * Example:
+             *
+             * ```javascript
+             * this.options.someOption;
+             * ```
+             * 
+             **/
+            this.options = Physics.util.options();
+            this.options( options );
+
+            this._aabb = new Physics.aabb();
+        },
+        
+        /** related to: Physics.aabb
+         * Geometry#aabb( angle ) -> Object
+         * - angle (Number): The angle to rotate the geometry
+         * + (Object): Bounding box values
+         * 
+         * Get axis-aligned bounding box for this object (rotated by angle if specified).
+         **/
+        aabb: function( angle ){
+
+            return Physics.aabb.clone(this._aabb);
+        },
+
+        /**
+         * Geometry#getFarthestHullPoint( dir[, result] ) -> Physics.vector
+         * - dir (Physics.vector): Direction to look
+         * - result (Physics.vector): A vector to write result to. Speeds up calculations.
+         * + (Physics.vector): The farthest hull point in local coordinates
+         * 
+         * Get farthest point on the hull of this geometry
+         * along the direction vector `dir`
+         * returns local coordinates. Replaces result if provided.
+         *
+         * Assume all coordinates are relative to the geometry 
+         * centroid (IE: in the body frame).
+         * 
+         * This should take a direction vector then it should
+         * calculate the location (in that frame of reference)
+         * of the point on the perimeter (hull) if you traveled
+         * in a straight line from the centroid in the provided
+         * direction. The result should be returned/set just like
+         * it is in the other geometries.
+         **/
+        getFarthestHullPoint: function( dir, result ){
+
+            result = result || new Physics.vector();
+
+            // not implemented.
+            return result.set( 0, 0 );
+        },
+
+        /** related to: Geometry#getFarthestHullPoint
+         * Geometry#getFarthestCorePoint( dir[, result] ) -> Physics.vector
+         * - dir (Physics.vector): Direction to look
+         * - result (Physics.vector): A vector to write result to. Speeds up calculations.
+         * + (Physics.vector): The farthest hull point in local coordinates
+         * 
+         * Get farthest point on the core shape of this geometry
+         * along the direction vector `dir`
+         * returns local coordinates. Replaces result if provided.
+         *
+         * This does almost the same thing as [[Geometry#getFarthestHullPoint]]
+         * but shrinks the shape by subtracting "margin" from it.
+         * Return the position of the point on the "core" shape.
+         **/
+        getFarthestCorePoint: function( dir, result, margin ){
+
+            result = result || new Physics.vector();
+
+            // not implemented.
+            return result.set( 0, 0 );
+        }
+    });
+
+}());
+
+// ---
+// inside: src/core/geometry-helpers.js
+
+/*
+ * Geometry helper functions
+ */
+
+/**
+ * Physics.geometry.regularPolygonVertices( sides, radius ) -> Array
+ * - sides (Number): Number of sides the polygon has
+ * - radius (Number): Size from center to a vertex
+ * + (Array): A list of [[Vectorish]] objects representing the vertices
+ *
+ * Generate a list of vertices for a regular polygon of any number of sides.
+ **/
+Physics.geometry.regularPolygonVertices = function( sides, radius ){
+    var verts = []
+        ,angle = Math.PI * 2 / sides
+        ,a = 0
+        ,i
+        ;
+
+    for ( i = 0; i < sides; i++ ){
+        verts.push({
+            x: radius * Math.cos( a )
+            ,y: radius * Math.sin( a )
+        });
+
+        a += angle;
+    }
+
+    return verts;
+};
+
+/**
+ * Physics.geometry.isPolygonConvex( hull ) -> Boolean
+ * - hull (Array): Array of ([[Vectorish]]) vertices
+ * + (Boolean): `true` if the polygon is convex. `false` otherwise.
+ *
+ * Determine if polygon hull is convex
+ **/
+Physics.geometry.isPolygonConvex = function( hull ){
+
+    var scratch = Physics.scratchpad()
+        ,prev = scratch.vector()
+        ,next = scratch.vector()
+        ,tmp = scratch.vector()
+        ,ret = true
+        ,sign = false
+        ,l = hull.length
+        ;
+
+    if ( !hull || !l ){
+        return false;
+    }
+
+    if ( l < 3 ){
+        // it must be a point or a line...
+        // which are convex
+        scratch.done();
+        return ret;
+    }
+
+    prev.clone( hull[ 0 ] ).vsub( tmp.clone( hull[ l - 1 ] ) );
+
+    // loop over the edges of the hull and construct vectors of the current
+    // edge and retain the last edge
+    // add two to the length to do a full cycle
+    for ( var i = 1; i <= l; ++i ){
+
+        next.clone( hull[ i % l ] ).vsub( tmp.clone( hull[ (i - 1) % l ] ) );
+
+        if ( sign === false ){
+
+            // first check the sign of the first cross product
+            sign = prev.cross( next );
+
+        } else if ( (sign > 0) ^ (prev.cross( next ) > 0) ){
+
+            // if the cross products are different signs it's not convex
+            ret = false;
+            break;
+        }
+
+        // remember the last edge
+        next.swap( prev );
+    }
+
+    scratch.done();
+    return ret;
+};
+
+/**
+ * Physics.geometry.getPolygonMOI( hull ) -> Number
+ * - hull (Array): Array of ([[Vectorish]]) vertices
+ * + (Number): The polygon's moment of inertia
+ *
+ * Gets the moment of inertia of a convex polygon
+ *
+ * See [List of moments of inertia](http://en.wikipedia.org/wiki/List_of_moments_of_inertia)
+ * for more information.
+ *
+ * _Note_: we make the following assumpations:
+ * * mass is unitary (== 1)
+ * * axis of rotation is the origin
+ **/
+Physics.geometry.getPolygonMOI = function( hull ){
+
+    var scratch = Physics.scratchpad()
+        ,prev = scratch.vector()
+        ,next = scratch.vector()
+        ,num = 0
+        ,denom = 0
+        ,tmp
+        ,l = hull.length
+        ;
+
+    if ( l < 2 ){
+        // it must be a point
+        // moi = 0
+        scratch.done();
+        return 0;
+    }
+
+    if ( l === 2 ){
+        // it's a line
+        // get length squared
+        tmp = next.clone( hull[ 1 ] ).distSq( prev.clone( hull[ 0 ] ) );
+        scratch.done();
+        return tmp / 12;
+    }
+
+    prev.clone( hull[ 0 ] );
+
+    for ( var i = 1; i < l; ++i ){
+
+        next.clone( hull[ i ] );
+
+        tmp = Math.abs( next.cross( prev ) );
+        num += tmp * ( next.normSq() + next.dot( prev ) + prev.normSq() );
+        denom += tmp;
+
+        prev.swap( next );
+    }
+
+    scratch.done();
+    return num / ( 6 * denom );
+};
+
+/**
+ * Physics.geometry.isPointInPolygon( pt, hull ) -> Boolean
+ * - pt (Vectorish): The point to test
+ * - hull (Array): Array of ([[Vectorish]]) vertices
+ * + (Boolean): `true` if point `pt` is inside the polygon
+ *
+ * Check if point is inside polygon hull.
+ **/
+Physics.geometry.isPointInPolygon = function( pt, hull ){
+
+    var scratch = Physics.scratchpad()
+        ,point = scratch.vector().clone( pt )
+        ,prev = scratch.vector()
+        ,next = scratch.vector()
+        ,ang = 0
+        ,l = hull.length
+        ;
+
+    if ( l < 2 ){
+        // it's a point...
+        ang = point.equals( prev.clone( hull[ 0 ] ));
+        scratch.done();
+        return ang;
+    }
+
+    if ( l === 2 ){
+        // it's a line
+        ang = point.angle( prev.clone( hull[ 0 ] ));
+        ang += point.angle( prev.clone( hull[ 1 ] ));
+        scratch.done();
+        return ( Math.abs(ang) === Math.PI );
+    }
+
+    prev.clone( hull[ 0 ] ).vsub( point );
+
+    // calculate the sum of angles between vector pairs
+    // from point to vertices
+    for ( var i = 1; i <= l; ++i ){
+
+        next.clone( hull[ i % l ] ).vsub( point );
+        ang += next.angle( prev );
+        prev.swap( next );
+    }
+
+    scratch.done();
+    return ( Math.abs(ang) > 1e-6 );
+};
+
+/**
+ * Physics.geometry.getPolygonArea( hull ) -> Number
+ * - hull (Array): Array of ([[Vectorish]]) vertices
+ * + (Number): The area (positive for clockwise ordering)
+ *
+ * Get the signed area of the polygon.
+ **/
+Physics.geometry.getPolygonArea = function getPolygonArea( hull ){
+
+    var scratch = Physics.scratchpad()
+        ,prev = scratch.vector()
+        ,next = scratch.vector()
+        ,ret = 0
+        ,l = hull.length
+        ;
+
+    if ( l < 3 ){
+        // it must be a point or a line
+        // area = 0
+        scratch.done();
+        return 0;
+    }
+
+    prev.clone( hull[ l - 1 ] );
+
+    for ( var i = 0; i < l; ++i ){
+
+        next.clone( hull[ i ] );
+
+        ret += prev.cross( next );
+
+        prev.swap( next );
+    }
+
+    scratch.done();
+    return ret / 2;
+};
+
+/**
+ * Physics.geometry.getPolygonCentroid( hull ) -> Physics.vector
+ * - hull (Array): Array of ([[Vectorish]]) vertices
+ * + (Physics.vector): The centroid
+ *
+ * Get the coordinates of the centroid.
+ **/
+Physics.geometry.getPolygonCentroid = function getPolygonCentroid( hull ){
+
+    var scratch = Physics.scratchpad()
+        ,prev = scratch.vector()
+        ,next = scratch.vector()
+        ,ret = new Physics.vector()
+        ,tmp
+        ,l = hull.length
+        ;
+
+    if ( l < 2 ){
+        // it must be a point
+        scratch.done();
+        return new Physics.vector( hull[0] );
+    }
+
+    if ( l === 2 ){
+        // it's a line
+        // get the midpoint
+        scratch.done();
+        return new Physics.vector((hull[ 1 ].x + hull[ 0 ].x)/2, (hull[ 1 ].y + hull[ 0 ].y)/2 );
+    }
+
+    prev.clone( hull[ l - 1 ] );
+
+    for ( var i = 0; i < l; ++i ){
+
+        next.clone( hull[ i ] );
+
+        tmp = prev.cross( next );
+        prev.vadd( next ).mult( tmp );
+        ret.vadd( prev );
+
+        prev.swap( next );
+    }
+
+    tmp = 1 / (6 * Physics.geometry.getPolygonArea( hull ));
+
+    scratch.done();
+    return ret.mult( tmp );
+};
+
+/**
+ * Physics.geometry.nearestPointOnLine( pt, linePt1, linePt2 ) -> Physics.vector
+ * - pt (Vectorish): The point
+ * - linePt1 (Vectorish): The first endpoint of the line
+ * - linePt2 (Vectorish): The second endpoint of the line
+ * + (Vector): The closest point
+ *
+ * Get the closest point on a discrete line to specified point.
+ **/
+Physics.geometry.nearestPointOnLine = function nearestPointOnLine( pt, linePt1, linePt2 ){
+
+    var scratch = Physics.scratchpad()
+        ,p = scratch.vector().clone( pt )
+        ,A = scratch.vector().clone( linePt1 ).vsub( p )
+        ,L = scratch.vector().clone( linePt2 ).vsub( p ).vsub( A )
+        ,lambdaB
+        ,lambdaA
+        ;
+
+    if ( L.equals(Physics.vector.zero) ){
+        // oh.. it's a zero vector. So A and B are both the closest.
+        // just use one of them
+        scratch.done();
+        return new Physics.vector( linePt1 );
+    }
+
+    lambdaB = - L.dot( A ) / L.normSq();
+    lambdaA = 1 - lambdaB;
+
+    if ( lambdaA <= 0 ){
+        // woops.. that means the closest simplex point
+        // isn't on the line it's point B itself
+        scratch.done();
+        return new Physics.vector( linePt2 );
+    } else if ( lambdaB <= 0 ){
+        // vice versa
+        scratch.done();
+        return new Physics.vector( linePt1 );
+    }
+
+    // guess we'd better do the math now...
+    p = new Physics.vector( linePt2 ).mult( lambdaB ).vadd( A.clone( linePt1 ).mult( lambdaA ) );
+    scratch.done();
+    return p;
+};
+
+
+// ---
+// inside: src/core/integrator.js
+
+(function(){
+
+    var defaults = {
+
+        // drag applied during integration
+        // 0 means vacuum
+        // 0.9 means molasses
+        drag: 0
+    };
+
+    /** related to: Physics.util.decorator
+     * Physics.integrator( name[, options] ) -> Integrator
+     * - name (String): The name of the integrator to create
+     * - options (Object): The configuration for that integrator ( depends on integrator ).
+       Available options and defaults:
+
+       ```javascript
+        {
+            // drag applied during integration
+            // 0 means vacuum
+            // 0.9 means molasses
+            drag: 0
+        }
+       ```
+     *
+     * Factory function for creating Integrators.
+     *
+     * Visit [the PhysicsJS wiki on Integrators](https://github.com/wellcaffeinated/PhysicsJS/wiki/Integrators)
+     * for usage documentation.
+     **/
+    Physics.integrator = Decorator('integrator', {
+
+        /** belongs to: Physics.integrator
+         * class Integrator
+         *
+         * The base class for integrators created by [[Physics.integrator]] factory function.
+         **/
+
+        /** internal
+         * Integrator#init( options )
+         * - options (Object): The configuration options passed by the factory
+         *
+         * Initialization. Internal use.
+         **/
+        init: function( options ){
+
+            /** related to: Physics.util.options
+             * Integrator#options( options ) -> Object
+             * - options (Object): The options to set as an object
+             * + (Object): The options
+             *
+             * Set options on this instance.
+             *
+             * Access options directly from the options object.
+             *
+             * Example:
+             *
+             * ```javascript
+             * this.options.someOption;
+             * ```
+             *
+             **/
+            this.options = Physics.util.options( defaults );
+            this.options( options );
+        },
+
+        /**
+         * Integrator#setWorld( world ) -> this
+         * - world (Object): The world (or null)
+         *
+         * Set which world to apply to.
+         *
+         * Usually this is called internally. Shouldn't be a need to call this yourself usually.
+         **/
+        setWorld: function( world ){
+
+            if ( this.disconnect && this._world ){
+                this.disconnect( this._world );
+            }
+
+            this._world = world;
+
+            if ( this.connect && world ){
+                this.connect( world );
+            }
+
+            return this;
+        },
+
+        /**
+         * Integrator#integrate( bodies, dt ) -> this
+         * - bodies (Array): List of bodies to integrate
+         * - dt (Number): Timestep size
+         *
+         * Integrate bodies by timestep.
+         *
+         * Will emit `integrate:velocities` and `integrate:positions`
+         * events on the world.
+         **/
+        integrate: function( bodies, dt ){
+
+            var world = this._world;
+
+            this.integrateVelocities( bodies, dt );
+
+            if ( world ){
+                world.emit('integrate:velocities', {
+                    bodies: bodies,
+                    dt: dt
+                });
+            }
+
+            this.integratePositions( bodies, dt );
+
+            if ( world ){
+                world.emit('integrate:positions', {
+                    bodies: bodies,
+                    dt: dt
+                });
+            }
+
+            return this;
+        },
+
+        /**
+         * Integrator#connect( world )
+         * - world (Physics.world): The world to connect to
+         *
+         * Connect to a world.
+         *
+         * Extend this when creating integrators if you need to specify pubsub management.
+         * Automatically called when added to world by the [[Integrator#setWorld]] method.
+         **/
+        connect: null,
+
+        /**
+         * Integrator#disconnect( world )
+         * - world (Physics.world): The world to disconnect from
+         *
+         * Disconnect from a world.
+         *
+         * Extend this when creating integrators if you need to specify pubsub management.
+         * Automatically called when added to world by the [[Integrator#setWorld]] method.
+         **/
+        disconnect: null,
+
+        /**
+         * Integrator#integrateVelocities( bodies, dt )
+         * - bodies (Array): List of bodies to integrate
+         * - dt (Number): Timestep size
+         *
+         * Just integrate the velocities.
+         *
+         * Should be overridden when creating integrators.
+         **/
+        integrateVelocities: function( bodies, dt ){
+
+            throw 'The integrator.integrateVelocities() method must be overriden';
+        },
+
+        /**
+         * Integrator#integratePositions( bodies, dt )
+         * - bodies (Array): List of bodies to integrate
+         * - dt (Number): Timestep size
+         *
+         * Just integrate the positions.
+         *
+         * Called after [[Integrator#integrateVelocities]].
+         *
+         * Should be overridden when creating integrators.
+         **/
+        integratePositions: function( bodies, dt ){
+
+            throw 'The integrator.integratePositions() method must be overriden';
+        }
+    });
+
+}());
+
+
+// ---
+// inside: src/core/renderer.js
+
+(function(){
+
+    var defaults = {
+        // draw meta data (fps, steps, etc)
+        meta: false,
+        // refresh rate of meta info
+        metaRefresh: 200,
+
+        // width of viewport
+        width: 600,
+        // height of viewport
+        height: 600,
+        // automatically resize the renderer
+        autoResize: true
+    };
+
+    /** related to: Physics.util.decorator
+     * Physics.renderer( name[, options] ) -> Renderer
+     * - name (String): The name of the renderer to create
+     * - options (Object): The configuration for that renderer ( depends on renderer ).
+       Available options and defaults:
+
+       ```javascript
+        {
+            // draw meta data (fps, steps, etc)
+            meta: false,
+            // refresh rate of meta info
+            metaRefresh: 200,
+
+            // width of viewport
+            width: 600,
+            // height of viewport
+            height: 600
+            // automatically resize the renderer
+            autoResize: true
+        }
+       ```
+     *
+     * Factory function for creating Renderers.
+     *
+     * Visit [the PhysicsJS wiki on Renderers](https://github.com/wellcaffeinated/PhysicsJS/wiki/Renderers)
+     * for usage documentation.
+     **/
+    Physics.renderer = Decorator('renderer', {
+
+        /** belongs to: Physics.renderer
+         * class Renderer
+         *
+         * The base class for renderers created by [[Physics.renderer]] factory function.
+         **/
+
+        /** internal
+         * Renderer#init( options )
+         * - options (Object): The configuration options passed by the factory
+         *
+         * Initialization. Internal use.
+         **/
+        init: function( options ){
+
+            var self = this
+                ,el = typeof options.el === 'string' ? document.getElementById(options.el) : options.el
+                ;
+
+            this.options = Physics.util.options(defaults);
+            this.options( options );
+
+            this.el = el ? el : document.body;
+            this.container = el && el.parentNode ? el.parentNode : document.body;
+            this.drawMeta = Physics.util.throttle( Physics.util.bind(this.drawMeta, this), this.options.metaRefresh );
+
+            window.addEventListener('resize', Physics.util.throttle(function(){
+                if ( self.options.autoResize ){
+                    self.resize();
+                }
+            }), 100);
+        },
+
+        /**
+         * Renderer#resize( [width, height] ) -> this
+         * - width (Number): The width in px
+         * - height (Number): The height in px
+         *
+         * Set the dimensions of the renderer.
+         *
+         * If no dimensions are specified it will auto resize.
+         **/
+        resize: function( width, height ){
+
+            if ( width === undefined && height === undefined ){
+                width = this.container.offsetWidth;
+                height = this.container.offsetHeight;
+            }
+
+            this.width = width || 0;
+            this.height = height || 0;
+            // should be implemented in renderers
+        },
+
+        /**
+         * Renderer#setWorld( world ) -> this
+         * - world (Object): The world (or null)
+         *
+         * Set which world to apply to.
+         *
+         * Usually this is called internally. Shouldn't be a need to call this yourself usually.
+         **/
+        setWorld: function( world ){
+
+            if ( this.disconnect && this._world ){
+                this.disconnect( this._world );
+            }
+
+            this._world = world;
+
+            if ( this.connect && world ){
+                this.connect( world );
+            }
+
+            return this;
+        },
+
+        /**
+         * Renderer#render( bodies, meta ) -> this
+         * - bodies (Array): Array of bodies in the world (by reference!)
+         * - meta (Object): meta information
+         *
+         * Render the world bodies and meta. Called by world.render()
+         **/
+        render: function( bodies, meta ){
+
+            var body
+                ,view
+                ,pos
+                ;
+
+            if (this.beforeRender){
+
+                this.beforeRender();
+            }
+
+            this._world.emit('beforeRender', {
+                renderer: this,
+                bodies: bodies,
+                meta: meta
+            });
+
+            if (this.options.meta){
+                this.drawMeta( meta );
+            }
+
+            this._interpolateTime = meta.interpolateTime;
+
+            for ( var i = 0, l = bodies.length; i < l; ++i ){
+
+                body = bodies[ i ];
+                view = body.view || ( body.view = this.createView(body.geometry, body.styles) );
+
+                if ( !body.hidden ){
+                    this.drawBody( body, view );
+                }
+            }
+
+            return this;
+        },
+
+        /**
+         * Renderer#createView( geometry, styles ) -> Mixed
+         * - geometry (Geometry): geometry The geometry
+         * - styles (Object|String): The styles configuration
+         * + (Mixed): Whatever the renderer needs to render the body.
+         *
+         * Create a view for the specified geometry.
+         *
+         * The view is used to render the body. It is a cached version
+         * of the body that gets moved and rotated according to the simulation.
+         *
+         * The styles are used to modify the appearance of the view.
+         * They depend on the renderer.
+         *
+         * Override this when creating renderers.
+         **/
+        createView: function( geometry, styles ){
+
+            // example:
+            // var el = document.createElement('div');
+            // el.style.height = geometry.height + 'px';
+            // el.style.width = geometry.width + 'px';
+            // return el;
+            throw 'You must override the renderer.createView() method.';
+        },
+
+        /**
+         * Renderer#drawMeta( meta )
+         * - meta (Object): The meta data
+         *
+         * Draw the meta data.
+         *
+         * The meta data will look like this:
+         *
+         * ```javascript
+         * meta = {
+         *     fps: 60, // the frames per second
+         *     ipf: 4 // the number of iterations per frame
+         * };
+         * ```
+         *
+         * Override this when creating renderers.
+         **/
+        drawMeta: function( meta ){
+
+            // example:
+            // this.els.fps.innerHTML = meta.fps.toFixed(2);
+            // this.els.steps.innerHTML = meta.steps;
+            throw 'You must override the renderer.drawMeta() method.';
+        },
+
+        /**
+         * Renderer#drawBody( body, view )
+         * - body (Object): The body to draw
+         * - view (Object): The view for the body
+         *
+         * Draw specified body using specified view.
+         *
+         * Override this when creating renderers.
+         **/
+        drawBody: function( body, view ){
+
+            // example (pseudocode):
+            // view.angle = body.state.angle
+            // view.position = body.state.position
+            throw 'You must override the renderer.drawBody() method.';
+        }
+
+
+    });
+
+}());
+
+
+// ---
+// inside: src/core/world.js
+
+/** related to: Physics
+ * class Physics.world
+ *
+ * The world class and factory function.
+ *
+ * Use [[Physics]] to create worlds.
+ **/
+(function(){
+
+    var execCallbacks = function execCallbacks( fns, scope, args ){
+
+        var fn
+            ,ret
+            ,cb = function(){
+                return execCallbacks( fns, scope, args );
+            }
+            ;
+
+        while ( fn = fns.shift() ){
+
+            ret = fn.apply(scope, args);
+
+            if (ret && ret.then){
+                return ret.then( cb );
+            }
+        }
+    };
+
+    var defaults = {
+
+        // default timestep
+        timestep: 6,
+        // maximum number of iterations per step
+        maxIPF: 4,
+        webworker: false, // NOT YET IMPLEMENTED
+
+        // default integrator
+        integrator: 'verlet',
+
+        // is sleeping disabled?
+        sleepDisabled: false,
+        // speed at which bodies wake up
+        sleepSpeedLimit: 0.05,
+        // variance in position below which bodies fall asleep
+        sleepVarianceLimit: 0.02,
+        // time (ms) before sleepy bodies fall asleep
+        sleepTimeLimit: 500
+    };
+
+    // begin world definitions
+
+    /** alias of: Physics
+     * new Physics.world([options, fn(world, Physics)])
+     * - options (Object): configuration options (see description)
+     * - fn (Function|Array): Callback function or array of callbacks called with this === world
+     * - world (Physics.world): The current world created
+     * - Physics (Physics): The Physics namespace
+     *
+     * World Constructor.
+     *
+     * Use [[Physics]] to create worlds.
+     *
+     * Configuration options and defaults:
+     *
+     * ```javascript
+     * {
+     *  // default timestep
+     *  timestep: 6,
+     *  // maximum number of iterations per step
+     *  maxIPF: 4,
+     *
+     *  // default integrator
+     *  integrator: 'verlet',
+     *
+     *  // is sleeping disabled?
+     *  sleepDisabled: false,
+     *  // speed at which bodies wake up
+     *  sleepSpeedLimit: 0.1,
+     *  // variance in position below which bodies fall asleep
+     *  sleepVarianceLimit: 2,
+     *  // time (ms) before sleepy bodies fall asleep
+     *  sleepTimeLimit: 500
+     * }
+     * ```
+     *
+     * If called with an array of functions, and any functions
+     * return a [promise-like object](http://promises-aplus.github.io/promises-spec/),
+     * each remaining callback will be called only when that promise is resolved.
+     *
+     * Example:
+     *
+     * ```javascript
+     * // hypothetical resources need to be loaded...
+     * Physics( cfg, [
+     *     function( world ){
+     *         var dfd = $.Deferred()
+     *             ,images = []
+     *             ,toLoad = myImages.length
+     *             ,callback = function(){
+     *                 toLoad--;
+     *                 // wait for all images to be loaded
+     *                 if ( toLoad <= 0 ){
+     *                     dfd.resolve();
+     *                 }
+     *             }
+     *             ;
+     *
+     *         // load images
+     *         $.each(myImages, function( src ){
+     *             var img = new Image();
+     *             img.onload = callback;
+     *             img.src = src;
+     *         });
+     *
+     *         return dfd.promise();
+     *     },
+     *     function( world ){
+     *         // won't be executed until images are loaded
+     *         // initialize world... etc...
+     *     }
+     * ]);
+     * ```
+     **/
+    var World = function World( cfg, fn ){
+
+        // allow creation of world without "new"
+        if (!(this instanceof World)){
+            return new World( cfg, fn );
+        }
+
+        this.init( cfg, fn );
+    };
+
+    // extend pubsub
+    World.prototype = Physics.util.extend({}, Physics.util.pubsub.prototype, {
+
+        /** internal, see: new Physics.world
+         * Physics.world#init( [options, fn(world, Physics)] )
+         * - options (Object): configuration options (see constructor)
+         * - fn (Function|Array): Callback function or array of callbacks called with this === world
+         *
+         * Initialization
+         **/
+        init: function( cfg, fn ){
+
+            var self = this;
+
+            if ( Physics.util.isFunction( cfg ) || Physics.util.isArray( cfg ) ){
+                fn = cfg;
+                cfg = {};
+            }
+
+            this._meta = {
+               // statistics (fps, etc)
+               fps: 0,
+               ipf: 0
+            };
+            this._bodies = [];
+            this._behaviors = [];
+            this._integrator = null;
+            this._renderer = null;
+            this._paused = false;
+            this._warp = 1;
+            this._time = 0;
+
+            // set options
+            this.options = Physics.util.options( defaults );
+            this.options.onChange(function( opts ){
+
+                // set timestep
+                self.timestep( opts.timestep );
+            });
+            this.options( cfg );
+
+            // add integrator
+            this.add(Physics.integrator( this.options.integrator ));
+
+            // apply the callback function
+            if ( Physics.util.isFunction( fn ) ){
+
+                execCallbacks([ fn ], this, [this, Physics] );
+
+            } else if ( Physics.util.isArray( fn ) ){
+
+                execCallbacks(fn, this, [this, Physics] );
+            }
+        },
+
+        /**
+         * Physics.world#options( cfg ) -> Object
+         * - options (Object): configuration options (see constructor)
+         * + (Object): Options container
+         *
+         * Set config options. Also access options by `.options.<option>`.
+         **/
+        options: null,
+
+        /** chainable
+         * Physics.world#add( things ) -> this
+         * - things (Object|Array): The thing, or array of things (body, behavior, integrator, or renderer) to add.
+         *
+         * Multipurpose add method. Add one or many bodies, behaviors, integrators, renderers...
+         **/
+        add: function( arg ){
+
+            var i = 0
+                ,len = arg && arg.length || 0
+                ,thing = Physics.util.isArray( arg ) ? arg[ 0 ] : arg
+                ;
+
+            if ( !thing ){
+                return this;
+            }
+
+            // we'll either cycle through an array
+            // or just run this on the arg itself
+            do {
+                switch (thing.type){
+
+                    case 'behavior':
+                        this.addBehavior(thing);
+                    break; // end behavior
+
+                    case 'integrator':
+                        this.integrator(thing);
+                    break; // end integrator
+
+                    case 'renderer':
+                        this.renderer(thing);
+                    break; // end renderer
+
+                    case 'body':
+                        this.addBody(thing);
+                    break; // end body
+
+                    default:
+                        throw 'Error: failed to add item of unknown type "'+ thing.type +'" to world';
+                    // end default
+                }
+
+            } while ( ++i < len && (thing = arg[ i ]) );
+
+            return this;
+        },
+
+        /** chainable
+         * Physics.world#remove( things ) -> this
+         * - things (Object|Array): The thing, or array of things (body, behavior, integrator, or renderer) to remove.
+         *
+         * Multipurpose remove method. Remove one or many bodies, behaviors, integrators, renderers...
+         **/
+        remove: function( arg ){
+
+            var i = 0
+                ,len = arg && arg.length || 0
+                ,thing = Physics.util.isArray( arg ) ? arg[ 0 ] : arg
+                ;
+
+            if ( !thing ){
+                return this;
+            }
+
+            // we'll either cycle through an array
+            // or just run this on the arg itself
+            do {
+                switch (thing.type){
+
+                    case 'behavior':
+                        this.removeBehavior( thing );
+                    break; // end behavior
+
+                    case 'integrator':
+                        if (thing === this._integrator){
+                            this.integrator( null );
+                        }
+                    break; // end integrator
+
+                    case 'renderer':
+                        if (thing === this._renderer){
+                            this.renderer( null );
+                        }
+                    break; // end renderer
+
+                    case 'body':
+                        this.removeBody( thing );
+                    break; // end body
+
+                    default:
+                        throw 'Error: failed to remove item of unknown type "'+ thing.type +'" from world';
+                    // end default
+                }
+
+            } while ( ++i < len && (thing = arg[ i ]) );
+
+            return this;
+        },
+
+        /** chainable
+         * Physics.world#has( thing ) -> Boolean
+         * - thing (Object): The thing to test
+         * + (Boolean): `true` if thing is in the world, `false` otherwise.
+         *
+         * Determine if a thing has been added to world.
+         **/
+        has: function( thing ){
+
+            var arr
+                ,i
+                ,l
+                ;
+
+            if ( !thing ){
+                return false;
+            }
+
+            switch (thing.type){
+
+                case 'behavior':
+                    arr = this._behaviors;
+                break; // end behavior
+
+                case 'integrator':
+                return ( this._integrator === thing );
+                // end integrator
+
+                case 'renderer':
+                return ( this._renderer === thing );
+                // end renderer
+
+                case 'body':
+                    arr = this._bodies;
+                break; // end body
+
+                default:
+                    throw 'Error: unknown type "'+ thing.type +'"';
+                // end default
+            }
+
+            // check array
+            return (Physics.util.indexOf( arr, thing ) > -1);
+        },
+
+        /** chainable
+         * Physics.world#integrator( [integrator] ) -> Integrator|this
+         * - integrator (Integrator): The integrator to set on the world
+         * + (Integrator): The currently set integrator if `integrator` not specified
+         * + (this): for chaining if `integrator` specified
+         *
+         * Get or Set the integrator
+         **/
+        integrator: function( integrator ){
+
+            if ( integrator === undefined ){
+                return this._integrator;
+            }
+
+            // do nothing if already added
+            if ( this._integrator === integrator ){
+                return this;
+            }
+
+            if ( this._integrator ){
+
+                this._integrator.setWorld( null );
+
+                this.emit( 'remove:integrator', {
+                    integrator: this._integrator
+                });
+            }
+
+            if ( integrator ){
+                this._integrator = integrator;
+                this._integrator.setWorld( this );
+
+                this.emit( 'add:integrator', {
+                    integrator: this._integrator
+                });
+            }
+
+            return this;
+        },
+
+        /** chainable
+         * Physics.world#renderer( [renderer] ) -> Renderer|this
+         * - renderer (Renderer): The renderer to set on the world
+         * + (Renderer): The currently set renderer if `renderer` not specified
+         * + (this): for chaining if `renderer` specified
+         *
+         * Get or Set the renderer
+         **/
+        renderer: function( renderer ){
+
+            if ( renderer === undefined ){
+                return this._renderer;
+            }
+
+            // do nothing if renderer already added
+            if ( this._renderer === renderer ){
+                return this;
+            }
+
+            if ( this._renderer ){
+
+                this._renderer.setWorld( null );
+
+                this.emit( 'remove:renderer', {
+                    renderer: this._renderer
+                });
+            }
+
+            if ( renderer ){
+                this._renderer = renderer;
+                this._renderer.setWorld( this );
+
+                this.emit( 'add:renderer', {
+                    renderer: this._renderer
+                });
+            }
+
+            return this;
+        },
+
+        /** chainable
+         * Physics.world#timestep( [dt] ) -> Number|this
+         * - dt (Number): The time step for the world
+         * + (Number): The currently set time step if `dt` not specified
+         * + (this): for chaining if `dt` specified
+         *
+         * Get or Set the timestep
+         **/
+        timestep: function( dt ){
+
+            if ( dt ){
+
+                this._dt = +dt.toPrecision(4); // only keep 4 decimal places of precision otherwise we get rounding errors
+                // calculate the maximum jump in time over which to do iterations
+                this._maxJump = dt * this.options.maxIPF;
+
+                return this;
+            }
+
+            return this._dt;
+        },
+
+        /** chainable
+         * Physics.world#wakeUpAll() -> this
+         * + (this): for chaining
+         *
+         * Wake up all bodies in world.
+         **/
+        wakeUpAll: function(){
+            var i = 0
+                ,l = this._bodies.length
+                ;
+
+            for ( i = 0; i < l; i++ ){
+                this._bodies[ i ].sleep( false );
+            }
+        },
+
+        /** chainable
+         * Physics.world#addBehavior( behavior ) -> this
+         * - behavior (Behavior): The behavior to add
+         *
+         * Add a behavior to the world
+         **/
+        addBehavior: function( behavior ){
+
+            var notify;
+
+            // don't allow duplicates
+            if ( this.has( behavior ) ){
+                return this;
+            }
+
+            behavior.setWorld( this );
+            this._behaviors.push( behavior );
+
+            this.emit( 'add:behavior', {
+                behavior: behavior
+            });
+
+            return this;
+        },
+
+        /**
+         * Physics.world#getBehaviors() -> Array
+         * + (Array): Array of behaviors
+         *
+         * Get copied list of behaviors in the world
+         **/
+        getBehaviors: function(){
+
+            // return the copied array
+            return [].concat(this._behaviors);
+        },
+
+        /** chainable
+         * Physics.world#removeBehavior( behavior ) -> this
+         * - behavior (Behavior): The behavior to remove
+         *
+         * Remove a behavior from the world
+         **/
+        removeBehavior: function( behavior ){
+
+            var behaviors = this._behaviors;
+
+            if (behavior){
+
+                for ( var i = 0, l = behaviors.length; i < l; ++i ){
+
+                    if (behavior === behaviors[ i ]){
+
+                        behaviors.splice( i, 1 );
+                        behavior.setWorld( null );
+
+                        this.emit( 'remove:behavior', {
+                            behavior: behavior
+                        });
+
+                        break;
+                    }
+                }
+            }
+
+            return this;
+        },
+
+        /** chainable
+         * Physics.world#addBody( body ) -> this
+         * - body (Body): The behavior to add
+         *
+         * Add a body to the world
+         **/
+        addBody: function( body ){
+
+            var notify;
+
+            // don't allow duplicates
+            if ( this.has( body ) ){
+                return this;
+            }
+
+            body.setWorld( this );
+            this._bodies.push( body );
+
+            this.emit( 'add:body', {
+                body: body
+            });
+
+            return this;
+        },
+
+        /**
+         * Physics.world#getBodies() -> Array
+         * + (Array): Array of bodies
+         *
+         * Get copied list of bodies in the world
+         **/
+        getBodies: function(){
+
+            // return the copied array
+            return [].concat(this._bodies);
+        },
+
+        /** chainable
+         * Physics.world#removeBody( body ) -> this
+         * - body (Body): The body to remove
+         *
+         * Remove a body from the world
+         **/
+        removeBody: function( body ){
+
+            var bodies = this._bodies;
+
+            if (body){
+
+                for ( var i = 0, l = bodies.length; i < l; ++i ){
+
+                    if (body === bodies[ i ]){
+
+                        bodies.splice( i, 1 );
+                        body.setWorld( null );
+
+                        this.emit( 'remove:body', {
+                            body: body
+                        });
+
+                        break;
+                    }
+                }
+            }
+
+            return this;
+        },
+
+        /** see: Physics.query
+         * Physics.world#findOne( rules ) -> Body | false
+         * Physics.world#findOne( filter(body) ) -> Body | false
+         * - rules (Object): Query rules.
+         * - filter (Function): Filter function called to check bodies
+         * - body (Body): Each body in the world
+         *
+         * Find first matching body based on query rules.
+         **/
+        findOne: function( rules ){
+
+            var self = this
+                ,fn = (typeof rules === 'function') ? rules : Physics.query( rules )
+                ;
+
+            return Physics.util.find( self._bodies, fn ) || false;
+        },
+
+        /** see: Physics.query
+         * Physics.world#find( rules ) -> Array
+         * Physics.world#find( filter(body) ) -> Array
+         * - rules (Object): Query rules
+         * - filter (Function): Filter function called to check bodies
+         * - body (Body): Each body in the world
+         *
+         * Find all matching bodies based on query rules.
+         **/
+        find: function( rules ){
+
+            var self = this
+                ,fn = (typeof rules === 'function') ? rules : Physics.query( rules )
+                ;
+
+            return Physics.util.filter( self._bodies, fn );
+        },
+
+        /** internal
+         * Physics.world#iterate( dt )
+         * - dt (Number): The timestep
+         *
+         * Do a single iteration.
+         **/
+        iterate: function( dt ){
+
+            this._integrator.integrate( this._bodies, dt );
+        },
+
+        /** chainable
+         * Physics.world#step( [now] ) -> this
+         * - now (Number): Current unix timestamp
+         *
+         * Step the world up to specified time or do one step if no time is specified.
+         **/
+        step: function( now ){
+
+            var time = this._time
+                ,warp = this._warp
+                ,invWarp = 1 / warp
+                ,dt = this._dt
+                ,animDt = dt * invWarp
+                ,animMaxJump = this._maxJump * invWarp
+                ,animDiff
+                ,worldDiff
+                ,target
+                ,meta = this._meta
+                ;
+
+            // if it's paused, don't step
+            // or if it's the first step...
+            if ( this._paused || this._animTime === undefined ){
+                this._animTime = now || this._animTime || Physics.util.ticker.now();
+
+                if ( !this._paused ){
+                    this.emit('step', meta);
+                }
+                return this;
+            }
+
+            // new time is specified, or just one iteration ahead
+            now = now || (this._animTime + animDt);
+            // the time between this step and the last
+            animDiff = now - this._animTime;
+
+            // if the time difference is too big... adjust
+            if ( animDiff > animMaxJump ){
+                this._animTime = now - animMaxJump;
+                animDiff = animMaxJump;
+            }
+
+            // the "world" time between this step and the last. Adjusts for warp
+            worldDiff = animDiff * warp;
+
+            // the target time for the world time to step to
+            target = time + worldDiff - dt;
+
+            this.emit('beforeStep');
+
+            if ( time <= target ){
+
+                while ( time <= target ){
+                    // increment world time
+                    time += dt;
+                    // increment animation time
+                    this._animTime += animDt;
+                    // record the world time
+                    this._time = time;
+                    // iterate by one timestep
+                    this.iterate( dt );
+                }
+            }
+
+            // set some meta
+            meta.fps = 1000 / (now - this._lastTime); // frames per second
+            meta.ipf = (worldDiff / dt).toFixed(2); // iterations per frame
+            meta.interpolateTime = dt + target - time;
+
+            // record the time this was called
+            this._lastTime = now;
+
+            this.emit('step', meta);
+            return this;
+        },
+
+        /**
+         * Physics.world#warp( [warp] ) -> this|Number
+         * - warp (Number): The time warp factor
+         *
+         * Speed up or slow down the iteration by this factor.
+         *
+         * Example:
+         * ```javascript
+         * // slow motion... 10x slower
+         * world.warp( 0.01 );
+         * ```
+         **/
+        warp: function( warp ){
+            if ( warp === undefined ){
+                return this._warp;
+            }
+
+            this._warp = warp || 1;
+
+            return this;
+        },
+
+        /** chainable
+         * Physics.world#render() -> this
+         *
+         * Render current world state using the renderer
+         **/
+        render: function(){
+
+            if ( !this._renderer ){
+                throw "No renderer added to world";
+            }
+
+            this._renderer.render( this._bodies, this._meta );
+            this.emit('render', {
+                bodies: this._bodies,
+                meta: this._meta,
+                renderer: this._renderer
+            });
+            return this;
+        },
+
+        /** chainable
+         * Physics.world#pause() -> this
+         *
+         * Pause the world (step calls do nothing).
+         **/
+        pause: function(){
+
+            this._paused = true;
+            this.emit('pause');
+            return this;
+        },
+
+        /** chainable
+         * Physics.world#unpause() -> this
+         *
+         * Unpause the world (step calls continue as usual).
+         **/
+        unpause: function(){
+
+            this._paused = false;
+            this.emit('unpause');
+            return this;
+        },
+
+        /**
+         * Physics.world#isPaused() -> Boolean
+         * + (Boolean): Returns `true` if world is paused, `false` otherwise.
+         *
+         * Determine if world is paused.
+         **/
+        isPaused: function(){
+
+            return !!this._paused;
+        },
+
+        /**
+         * Physics.world#destroy()
+         *
+         * Destroy the world.
+         * (Bwahahahahaha!)
+         **/
+        destroy: function(){
+
+            var self = this;
+            self.pause();
+
+            // notify before
+            this.emit('destroy');
+
+            // remove all listeners
+            self.off( true );
+            // remove everything
+            self.remove( self.getBodies() );
+            self.remove( self.getBehaviors() );
+            self.integrator( null );
+            self.renderer( null );
+        }
+
+    });
+
+    Physics.world = World;
+
+}());
+
+
+// ---
+// inside: src/integrators/verlet.js
+
+Physics.integrator('verlet', function( parent ){
+
+    // for this integrator we need to know if the object has been integrated before
+    // so let's add a mixin to bodies
+
+    Physics.body.mixin({
+
+        started: function( val ){
+            if ( val !== undefined ){
+                this._started = true;
+            }
+
+            return !!this._started;
+        }
+    });
+
+
+    return {
+        /**
+         * class Verlet < Integrator
+         *
+         * `Physics.integrator('verlet')`.
+         *
+         * The verlet integrator.
+         **/
+
+        // extended
+        init: function( options ){
+
+            // call parent init
+            parent.init.call(this, options);
+        },
+
+        // extended
+        integrateVelocities: function( bodies, dt ){
+
+            // half the timestep
+            var dtdt = dt * dt
+                ,drag = 1 - this.options.drag
+                ,body = null
+                ,state
+                ,prevDt = this.prevDt || dt
+                ,dtMul = (dtdt + dt * prevDt) * 0.5
+                ;
+
+            for ( var i = 0, l = bodies.length; i < l; ++i ){
+
+                body = bodies[ i ];
+                state = body.state;
+
+                // only integrate if the body isn't static
+                if ( body.treatment !== 'static' && !body.sleep( dt ) ){
+
+                    // Inspired from https://github.com/soulwire/Coffee-Physics
+                    // @licence MIT
+                    //
+                    // v = x - ox
+                    // x = x + (v + a * dt * dt)
+
+                    // use the velocity in vel if the velocity has been changed manually
+                    if (state.vel.equals( state.old.vel ) && body.started()){
+
+                        // Get velocity by subtracting old position from curr position
+                        state.vel.clone( state.pos ).vsub( state.old.pos );
+
+                    } else {
+
+                        state.old.pos.clone( state.pos ).vsub( state.vel );
+                        // so we need to scale the value by dt so it
+                        // complies with other integration methods
+                        state.vel.mult( dt );
+                    }
+
+                    // Apply "air resistance".
+                    if ( drag ){
+
+                        state.vel.mult( drag );
+                    }
+
+                    // Apply acceleration
+                    // v += a * dt * dt
+                    state.vel.vadd( state.acc.mult( dtMul ) );
+
+                    // restore velocity
+                    state.vel.mult( 1/dt );
+
+                    // store calculated velocity
+                    state.old.vel.clone( state.vel );
+
+                    // Reset accel
+                    state.acc.zero();
+
+                    //
+                    // Angular components
+                    //
+
+                    if (state.angular.vel === state.old.angular.vel && body.started()){
+
+                        state.angular.vel = (state.angular.pos - state.old.angular.pos);
+
+                    } else {
+
+                        state.old.angular.pos = state.angular.pos - state.angular.vel;
+                        state.angular.vel *= dt;
+                    }
+
+                    state.angular.vel += state.angular.acc * dtMul;
+                    state.angular.vel /= dt;
+                    state.old.angular.vel = state.angular.vel;
+                    state.angular.acc = 0;
+
+                    body.started( true );
+
+                } else {
+                    // set the velocity and acceleration to zero!
+                    state.vel.zero();
+                    state.acc.zero();
+                    state.angular.vel = 0;
+                    state.angular.acc = 0;
+                }
+            }
+        },
+
+        // extended
+        integratePositions: function( bodies, dt ){
+
+            // half the timestep
+            var dtdt = dt * dt
+                ,body = null
+                ,state
+                ,prevDt = this.prevDt || dt
+                ,dtcorr = dt/prevDt
+                ;
+
+            for ( var i = 0, l = bodies.length; i < l; ++i ){
+
+                body = bodies[ i ];
+                state = body.state;
+
+                // only integrate if the body isn't static
+                if ( body.treatment !== 'static' && !body.sleep() ){
+
+                    // so we need to scale the value by dt so it
+                    // complies with other integration methods
+                    state.vel.mult( dt * dtcorr );
+
+                    // Store old position.
+                    // xold = x
+                    state.old.pos.clone( state.pos );
+
+                    state.pos.vadd( state.vel );
+
+                    // restore velocity
+                    state.vel.mult( 1 / (dt * dtcorr) );
+
+                    // store calculated velocity
+                    state.old.vel.clone( state.vel );
+
+                    //
+                    // Angular components
+                    //
+
+
+                    state.angular.vel *= dt * dtcorr;
+
+                    state.old.angular.pos = state.angular.pos;
+
+                    state.angular.pos += state.angular.vel;
+                    state.angular.vel /= dt * dtcorr;
+                    state.old.angular.vel = state.angular.vel;
+                }
+            }
+
+            this.prevDt = dt;
+        }
+    };
+});
+
+
+// ---
+// inside: src/geometries/point.js
+
+/** alias of: Geometry
+ * class PointGeometry < Geometry
+ *
+ * Physics.geometry('point')
+ *
+ * The point geometry represents a point.
+ **/
+Physics.geometry('point', function( parent ){});
+
+
+// ---
+// inside: src/bodies/point.js
+
+/** alias of: Body
+ * class PointBody < Body
+ *
+ * Physics.body('point')
+ *
+ * The point body represents a point.
+ **/
+Physics.body('point', function( parent ){
+    return {
+        init: function( opts ){
+            parent.init.call( this, opts );
+            this.moi = 0;
+        }
+    };
+});
+
+
+// ---
+// inside: src/geometries/circle.js
+
+/** 
+ * class CircleGeometry < Geometry
+ *
+ * Physics.geometry('circle')
+ *
+ * The circle geometry has a circular shape.
+ *
+ * Additional options include:
+ * - radius: the radius
+ *
+ * Example:
+ *
+ * ```javascript
+ * var round = Physics.body('circle', {
+ *     x: 30,
+ *     y: 20,
+ *     radius: 5
+ * });
+ * ```
+ **/
+Physics.geometry('circle', function( parent ){
+
+    var defaults = {
+
+        radius: 1.0
+    };
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            var self = this;
+            // call parent init method
+            parent.init.call(this, options);
+
+            this.options.defaults( defaults );
+            this.options.onChange(function( opts ){
+                this.radius = opts.radius;
+            });
+            this.options( options );
+
+            this._aabb = Physics.aabb();
+            this.radius = this.options.radius;
+        },
+                
+        // extended
+        aabb: function( angle ){
+
+            var r = this.radius
+                ;
+
+            // circles are symetric... so angle has no effect
+            if ( this._aabb.hw !== r ){
+                // recalculate
+                this._aabb = Physics.aabb( -r, -r, r, r );
+            }
+
+            return Physics.aabb.clone( this._aabb );
+        },
+
+        // extended
+        getFarthestHullPoint: function( dir, result ){
+
+            result = result || new Physics.vector();
+
+            return result.clone( dir ).normalize().mult( this.radius );
+        },
+
+        // extended
+        getFarthestCorePoint: function( dir, result, margin ){
+
+            result = result || new Physics.vector();
+
+            // we can use the center of the circle as the core object
+            // because we can project a point to the hull in any direction
+            // ... yay circles!
+            // but since the caller is expecting a certain margin... give it
+            // to them
+            return result.clone( dir ).normalize().mult( this.radius - margin );
+        }
+    };
+});
+
+
+// ---
+// inside: src/geometries/compound.js
+
+/**
+ * class CompoundGeometry < Geometry
+ *
+ * Physics.geometry('compound')
+ *
+ * Geometry for compound shapes.
+ *
+ * Example:
+ *
+ * ```javascript
+ * var thing = Physics.geometry('compound');
+ * thing.addChild( child, pos, rotation );
+ * ```
+ **/
+Physics.geometry('compound', function( parent ){
+
+    var defaults = {
+
+    };
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            var self = this;
+
+            // call parent init method
+            parent.init.call(this, options);
+
+            this.options.defaults( defaults );
+            this.options( options );
+
+            this.children = [];
+        },
+
+        /**
+         * CompoundGeometry#addChild( geometry, pos ) -> this
+         * - geometry (Geometry): The child to add.
+         * - pos (Physics.vector): The position to add the child at.
+         * - angle (Number): The rotation angle
+         *
+         * Add a child at relative position.
+         **/
+        addChild: function( geometry, pos, angle ){
+
+            this._aabb = null;
+            this.children.push({
+                g: geometry
+                ,pos: new Physics.vector( pos )
+                ,angle: angle
+            });
+
+            return this;
+        },
+
+        /**
+         * CompoundGeometry#clear() -> this
+         *
+         * Remove all children.
+         **/
+        clear: function(){
+
+            this._aabb = null;
+            this.children = [];
+
+            return this;
+        },
+
+        // extended
+        aabb: function( angle ){
+
+            if (!angle && this._aabb){
+                return Physics.aabb.clone( this._aabb );
+            }
+
+            var b
+                ,aabb
+                ,ch
+                ,ret
+                ,scratch = Physics.scratchpad()
+                ,pos = Physics.vector()
+                ;
+
+            angle = angle || 0;
+
+            for ( var i = 0, l = this.children.length; i < l; i++ ) {
+                ch = this.children[ i ];
+                // the aabb rotated by overall angle and the child rotation
+                aabb = ch.g.aabb( angle + ch.angle );
+                pos.clone( ch.pos );
+                if ( angle ){
+                    // get the child's position rotated if needed
+                    pos.rotate( angle );
+                }
+                // move the aabb to the child's position
+                aabb.x += pos._[0];
+                aabb.y += pos._[1];
+                ret = ret ? Physics.aabb.union(ret, aabb, true) : aabb;
+            }
+
+            if ( !angle ){
+                // if we don't have an angle specified (or it's zero)
+                // then we can cache this result
+                this._aabb = Physics.aabb.clone( ret );
+            }
+
+            return scratch.done( ret );
+        },
+
+        // extended
+        // NOTE: unlike other geometries this can't be used in the
+        // GJK algorithm because the shape isn't garanteed to be convex
+        getFarthestHullPoint: function( dir, result ){
+
+            var ch
+                ,i
+                ,l = this.children.length
+                ,scratch = Physics.scratchpad()
+                ,v = scratch.vector()
+                ,len = 0
+                ,maxlen = 0
+                ;
+
+            result = result || new Physics.vector();
+
+            // find the one with the largest projection along dir
+            for ( i = 0; i < l; i++ ) {
+                ch = this.children[ i ];
+                ch.g.getFarthestHullPoint( dir.rotate(-ch.angle), v );
+                len = v.rotate(ch.angle).vadd( ch.pos ).proj( dir.rotate(ch.angle) );
+
+                if ( len > maxlen ){
+                    maxlen = len;
+                    result.swap( v );
+                }
+            }
+
+            return scratch.done( result );
+        },
+
+        // extended
+        // NOTE: unlike other geometries this can't be used in the
+        // GJK algorithm because the shape isn't garanteed to be convex
+        getFarthestCorePoint: function( dir, result, margin ){
+
+            var ch
+                ,i
+                ,l = this.children.length
+                ,scratch = Physics.scratchpad()
+                ,v = scratch.vector()
+                ,len = 0
+                ,maxlen = 0
+                ;
+
+            result = result || new Physics.vector();
+
+            // find the one with the largest projection along dir
+            for ( i = 0; i < l; i++ ) {
+                ch = this.children[ i ];
+                ch.g.getFarthestCorePoint(dir.rotate(-ch.angle), v, margin );
+                len = v.rotate(ch.angle).vadd( ch.pos ).proj( dir.rotate(ch.angle) );
+
+                if ( len > maxlen ){
+                    maxlen = len;
+                    result.swap( v );
+                }
+            }
+
+            return scratch.done( result );
+        }
+    };
+});
+
+
+// ---
+// inside: src/geometries/convex-polygon.js
+
+/**
+ * class ConvexPolygonGeometry < Geometry
+ *
+ * Physics.geometry('convex-polygon')
+ *
+ * Geometry for convex polygons.
+ *
+ * Additional config options:
+ *
+ * - vertices: Array of [[Vectorish]] objects representing the polygon vertices in clockwise (or counterclockwise) order.
+ *
+ * Example:
+ *
+ * ```javascript
+ * var pentagon = Physics.geometry('convex-polygon', {
+ *     // the centroid is automatically calculated and used to position the shape
+ *     vertices: [
+ *         { x: 0, y: -30 },
+ *         { x: -29, y: -9 },
+ *         { x: -18, y: 24 },
+ *         { x: 18, y: 24 },
+ *         { x: 29, y: -9 }
+ *     ]
+ * });
+ * ```
+ **/
+Physics.geometry('convex-polygon', function( parent ){
+
+    var ERROR_NOT_CONVEX = 'Error: The vertices specified do not match that of a _convex_ polygon.';
+
+    var defaults = {
+
+    };
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            var self = this;
+
+            // call parent init method
+            parent.init.call(this, options);
+
+            this.options.defaults( defaults );
+            this.options.onChange(function( opts ){
+                self.setVertices( opts.vertices || [] );
+            });
+            this.options( options );
+
+            self.setVertices( this.options.vertices || [] );
+
+        },
+
+        /**
+         * ConvexPolygonGeometry#setVertices( hull ) -> this
+         * - hull (Array): Vertices represented by an array of [[Vectorish]] objects, in either clockwise or counterclockwise order
+         *
+         * Set the vertices of this polygon.
+         **/
+        setVertices: function( hull ){
+
+            var scratch = Physics.scratchpad()
+                ,transl = scratch.transform()
+                ,verts = this.vertices = []
+                ;
+
+            if ( !Physics.geometry.isPolygonConvex( hull ) ){
+                throw ERROR_NOT_CONVEX;
+            }
+
+            transl.setRotation( 0 );
+            transl.setTranslation( Physics.geometry.getPolygonCentroid( hull ).negate() );
+
+            // translate each vertex so that the centroid is at the origin
+            // then add the vertex as a vector to this.vertices
+            for ( var i = 0, l = hull.length; i < l; ++i ){
+
+                verts.push( new Physics.vector( hull[ i ] ).translate( transl ) );
+            }
+
+            this._area = Physics.geometry.getPolygonArea( verts );
+            this._aabb = false;
+            return scratch.done(this);
+        },
+
+        // extended
+        aabb: function( angle ){
+
+            if (!angle && this._aabb){
+                return Physics.aabb.clone( this._aabb );
+            }
+
+            var scratch = Physics.scratchpad()
+                ,p = scratch.vector()
+                ,trans = scratch.transform().setRotation( angle || 0 )
+                ,xaxis = scratch.vector().set( 1, 0 ).rotateInv( trans )
+                ,yaxis = scratch.vector().set( 0, 1 ).rotateInv( trans )
+                ,xmax = this.getFarthestHullPoint( xaxis, p ).proj( xaxis )
+                ,xmin = - this.getFarthestHullPoint( xaxis.negate(), p ).proj( xaxis )
+                ,ymax = this.getFarthestHullPoint( yaxis, p ).proj( yaxis )
+                ,ymin = - this.getFarthestHullPoint( yaxis.negate(), p ).proj( yaxis )
+                ,aabb
+                ;
+
+            aabb = Physics.aabb( xmin, ymin, xmax, ymax );
+
+            if (!angle){
+                // if we don't have an angle specified (or it's zero)
+                // then we can cache this result
+                this._aabb = Physics.aabb.clone( aabb );
+            }
+
+            scratch.done();
+            return aabb;
+        },
+
+        // extended
+        getFarthestHullPoint: function( dir, result, data ){
+
+            var verts = this.vertices
+                ,val
+                ,prev
+                ,l = verts.length
+                ,i = 2
+                ,idx
+                ;
+
+            result = result || new Physics.vector();
+
+            if ( l < 2 ){
+                if ( data ){
+                    data.idx = 0;
+                }
+                return result.clone( verts[0] );
+            }
+
+            prev = verts[ 0 ].dot( dir );
+            val = verts[ 1 ].dot( dir );
+
+            if ( l === 2 ){
+                idx = (val >= prev) ? 1 : 0;
+                if ( data ){
+                    data.idx = idx;
+                }
+                return result.clone( verts[ idx ] );
+            }
+
+            if ( val >= prev ){
+                // go up
+                // search until the next dot product
+                // is less than the previous
+                while ( i < l && val >= prev ){
+                    prev = val;
+                    val = verts[ i ].dot( dir );
+                    i++;
+                }
+
+                if (val >= prev){
+                    i++;
+                }
+
+                // return the previous (furthest with largest dot product)
+                idx = i - 2;
+                if ( data ){
+                    data.idx = i - 2;
+                }
+                return result.clone( verts[ idx ] );
+
+            } else {
+                // go down
+
+                i = l;
+                while ( i > 1 && prev >= val ){
+                    i--;
+                    val = prev;
+                    prev = verts[ i ].dot( dir );
+                }
+
+                // return the previous (furthest with largest dot product)
+                idx = (i + 1) % l;
+                if ( data ){
+                    data.idx = idx;
+                }
+                return result.clone( verts[ idx ] );
+            }
+        },
+
+        // extended
+        getFarthestCorePoint: function( dir, result, margin ){
+
+            var norm
+                ,scratch = Physics.scratchpad()
+                ,next = scratch.vector()
+                ,prev = scratch.vector()
+                ,verts = this.vertices
+                ,l = verts.length
+                ,mag
+                ,sign = this._area > 0
+                ,data = {}
+                ;
+
+            result = this.getFarthestHullPoint( dir, result, data );
+
+            // get normalized directions to next and previous vertices
+            next.clone( verts[ (data.idx + 1) % l ] ).vsub( result ).normalize().perp( sign );
+            prev.clone( verts[ (data.idx - 1 + l) % l ] ).vsub( result ).normalize().perp( !sign );
+
+            // get the magnitude of a vector from the result vertex
+            // that splits down the middle
+            // creating a margin of "m" to each edge
+            mag = margin / (1 + next.dot(prev));
+
+            result.vadd( next.vadd( prev ).mult( mag ) );
+            scratch.done();
+            return result;
+        }
+    };
+});
+
+
+// ---
+// inside: src/geometries/rectangle.js
+
+/**
+ * class RectangleGeometry < Geometry
+ *
+ * Physics.geometry('rectangle')
+ *
+ * Geometry for rectangles.
+ *
+ * Additional config options:
+ *
+ * - width: The width
+ * - height: The height
+ *
+ * Example:
+ *
+ * ```javascript
+ * var rectGeo = Physics.geometry('rectangle', {
+ *     width: 30,
+ *     height: 40
+ * });
+ * ```
+ **/
+Physics.geometry('rectangle', function( parent ){
+
+    var defaults = {
+
+    };
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            var self = this;
+
+            // call parent init method
+            parent.init.call(this, options);
+
+            this.options.defaults( defaults );
+            this.options.onChange(function( opts ){
+                /**
+                 * RectangleGeometry#width = Number
+                 *
+                 * The width
+                 **/
+                self.width = self.options.width || 1;
+                /**
+                 * RectangleGeometry#height = Number
+                 *
+                 * The height
+                 **/
+                self.height = self.options.height || 1;
+            });
+            this.options( options );
+        },
+
+        // extended
+        aabb: function( angle ){
+
+            if (!angle){
+                return Physics.aabb( this.width, this.height );
+            }
+
+            var scratch = Physics.scratchpad()
+                ,p = scratch.vector()
+                ,trans = scratch.transform().setRotation( angle || 0 )
+                ,xaxis = scratch.vector().set( 1, 0 ).rotateInv( trans )
+                ,yaxis = scratch.vector().set( 0, 1 ).rotateInv( trans )
+                ,xmax = this.getFarthestHullPoint( xaxis, p ).proj( xaxis )
+                ,xmin = - this.getFarthestHullPoint( xaxis.negate(), p ).proj( xaxis )
+                ,ymax = this.getFarthestHullPoint( yaxis, p ).proj( yaxis )
+                ,ymin = - this.getFarthestHullPoint( yaxis.negate(), p ).proj( yaxis )
+                ;
+
+            scratch.done();
+            return Physics.aabb( xmin, ymin, xmax, ymax );
+        },
+
+        // extended
+        getFarthestHullPoint: function( dir, result ){
+
+            result = result || new Physics.vector();
+
+            var x = dir.x
+                ,y = dir.y
+                ;
+
+            x = x === 0 ? 0 : x < 0 ? -this.width * 0.5 : this.width * 0.5;
+            y = y === 0 ? 0 : y < 0 ? -this.height * 0.5 : this.height * 0.5;
+
+            return result.set( x, y );
+        },
+
+        // extended
+        getFarthestCorePoint: function( dir, result, margin ){
+
+            var x, y;
+            result = this.getFarthestHullPoint( dir, result );
+            x = result.x;
+            y = result.y;
+            result.x = x === 0 ? 0 : x < 0 ? x + margin : x - margin;
+            result.y = y === 0 ? 0 : y < 0 ? y + margin : y - margin;
+
+            return result;
+        }
+    };
+});
+
+
+// ---
+// inside: src/bodies/circle.js
+
+/*
+ * @requires geometries/circle
+ */
+/** 
+ * class CircleBody < Body
+ *
+ * Physics.body('circle')
+ *
+ * The circle body has a circular shape.
+ *
+ * Additional options include:
+ * - radius: the radius
+ *
+ * Example:
+ *
+ * ```javascript
+ * var round = Physics.body('circle', {
+ *     x: 30,
+ *     y: 20,
+ *     radius: 5
+ * });
+ * ```
+ **/
+Physics.body('circle', function( parent ){
+
+    var defaults = {
+        radius: 1.0
+    };
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            // call parent init method
+            parent.init.call(this, options);
+
+            options = Physics.util.extend({}, defaults, options);
+
+            this.geometry = Physics.geometry('circle', {
+                radius: options.radius
+            });
+
+            this.recalc();
+        },
+
+        // extended
+        recalc: function(){
+            parent.recalc.call(this);
+            // moment of inertia
+            this.moi = this.mass * this.geometry.radius * this.geometry.radius / 2;
+        }
+    };
+});
+
+
+// ---
+// inside: src/bodies/compound.js
+
+/*
+ * @requires geometries/compound
+ */
+ /**
+  * class CompoundBody < Body
+  *
+  * Physics.body('compound')
+  *
+  * Not a body in itself. It's a container to group other bodies. The position of the body is the center of mass.
+  * It must have at least one child before being added to the world.
+  *
+  * Additional config options:
+  *
+  * - children: Array of [[Body]] objects.
+  *
+  * Example:
+  *
+  * ```javascript
+  * var thing = Physics.body('compound', {
+  *     // place the center of mass at (300, 200)
+  *     x: 300,
+  *     y: 200,
+  *     // the center of mass is automatically calculated and used to position the shape
+  *     children: [
+  *         body1,
+  *         body2,
+  *         // ...
+  *     ]
+  * });
+  * ```
+  **/
+Physics.body('compound', function( parent ){
+
+    var defaults = {
+
+    };
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            // call parent init method
+            parent.init.call(this, options);
+
+            this.mass = 0;
+            this.moi = 0;
+
+            this.children = [];
+            this.geometry = Physics.geometry('compound');
+            this.addChildren( options.children );
+        },
+
+        // extended
+        connect: function( world ){
+            // sanity check
+            if ( this.mass <= 0 ){
+                throw 'Can not add empty compound body to world.';
+            }
+        },
+
+        /**
+         * CompoundBody#addChild( body ) -> this
+         * - body (Body): The child to add
+         *
+         * Add a body as a child.
+         **/
+        addChild: function( body ){
+
+            this.addChildren([ body ]);
+            return this;
+        },
+
+        /**
+         * CompoundBody#addChildren( bodies ) -> this
+         * - bodies (Array): The list of children to add
+         *
+         * Add an array of children to the compound.
+         **/
+        addChildren: function( bodies ){
+
+            var self = this
+                ,scratch = Physics.scratchpad()
+                ,com = scratch.vector().zero()
+                ,b
+                ,pos
+                ,i
+                ,l = bodies && bodies.length
+                ,M = 0
+                ;
+
+            if ( !l ){
+                return scratch.done( this );
+            }
+
+            for ( i = 0; i < l; i++ ){
+                b = bodies[ i ];
+                // remove body from world if applicable
+                if ( b._world ){
+                    b._world.remove( b );
+                }
+                // add child
+                this.children.push( b );
+                // add child to geometry
+                this.geometry.addChild(
+                    b.geometry,
+                    new Physics.vector(b.offset)
+                        .rotate(b.state.angular.pos)
+                        .vadd(b.state.pos),
+                    b.state.angular.pos
+                );
+                // calc com contribution
+                pos = b.state.pos;
+                com.add( pos._[0] * b.mass, pos._[1] * b.mass );
+                M += b.mass;
+            }
+
+            // add mass
+            this.mass += M;
+            // com adjustment (assuming com is currently at (0,0) body coords)
+            com.mult( 1 / this.mass );
+
+            // shift the center of mass
+            this.offset.vsub( com );
+
+            // refresh view on next render
+            if ( this._world ){
+                this._world.one('render', function(){
+                    self.view = null;
+                });
+            }
+            this.recalc();
+
+            return scratch.done( this );
+        },
+
+        /**
+         * CompoundBody#clear() -> this
+         *
+         * Remove all children.
+         **/
+        clear: function(){
+
+            this._aabb = null;
+            this.moi = 0;
+            this.mass = 0;
+            this.offset.zero();
+            this.children = [];
+            this.geometry.clear();
+
+            return this;
+        },
+
+        /**
+         * CompoundBody#refreshGeometry() -> this
+         *
+         * If the children's positions change, `refreshGeometry()` should be called to fix the shape.
+         **/
+        refreshGeometry: function(){
+
+            this.geometry.clear();
+
+            for ( var i = 0, b, l = this.children.length; i < l; i++ ) {
+                b = this.children[ i ];
+                this.geometry.addChild( b.geometry, new Physics.vector(b.state.pos).vadd(b.offset), b.state.angular.pos );
+            }
+
+            return this;
+        },
+
+        // extended
+        recalc: function(){
+
+            parent.recalc.call(this);
+            // moment of inertia
+            var b
+                ,moi = 0
+                ;
+
+            for ( var i = 0, l = this.children.length; i < l; i++ ) {
+                b = this.children[ i ];
+                b.recalc();
+                // parallel axis theorem
+                moi += b.moi + b.mass * b.state.pos.normSq();
+            }
+
+            this.moi = moi;
+            return this;
+        }
+    };
+});
+
+
+// ---
+// inside: src/bodies/convex-polygon.js
+
+/*
+ * @requires geometries/convex-polygon
+ */
+ /**
+  * class ConvexPolygonBody < Body
+  *
+  * Physics.body('convex-polygon')
+  *
+  * Body for convex polygons. The position of the body is the centroid of the polygon.
+  *
+  * Additional config options:
+  *
+  * - vertices: Array of [[Vectorish]] objects representing the polygon vertices in clockwise (or counterclockwise) order.
+  *
+  * Example:
+  *
+  * ```javascript
+  * var pentagon = Physics.body('convex-polygon', {
+  *     // place the centroid of the polygon at (300, 200)
+  *     x: 300,
+  *     y: 200,
+  *     // the centroid is automatically calculated and used to position the shape
+  *     vertices: [
+  *         { x: 0, y: -30 },
+  *         { x: -29, y: -9 },
+  *         { x: -18, y: 24 },
+  *         { x: 18, y: 24 },
+  *         { x: 29, y: -9 }
+  *     ]
+  * });
+  * ```
+  **/
+Physics.body('convex-polygon', function( parent ){
+
+    var defaults = {
+
+    };
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            // call parent init method
+            parent.init.call(this, options);
+
+            options = Physics.util.extend({}, defaults, options);
+
+            this.geometry = Physics.geometry('convex-polygon', {
+                vertices: options.vertices
+            });
+
+            this.recalc();
+        },
+
+        // extended
+        recalc: function(){
+            parent.recalc.call(this);
+            // moment of inertia
+            this.moi = Physics.geometry.getPolygonMOI( this.geometry.vertices );
+        }
+    };
+});
+
+
+// ---
+// inside: src/bodies/rectangle.js
+
+/*
+ * @requires geometries/rectangle
+ */
+ /**
+  * class RectangleBody < Body
+  *
+  * Physics.body('rectangle')
+  *
+  * Body for rectangles. The position of the body is the centroid of the rectangle.
+  *
+  * Additional config options:
+  *
+  * - width: The width
+  * - height: The height
+  *
+  * Example:
+  *
+  * ```javascript
+  * var rect = Physics.body('rectangle', {
+  *     // place the centroid of the rectangle at (300, 200)
+  *     x: 300,
+  *     y: 200,
+  *     width: 30,
+  *     height: 40
+  * });
+  * ```
+  **/
+Physics.body('rectangle', function( parent ){
+
+    var defaults = {
+
+    };
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            // call parent init method
+            parent.init.call(this, options);
+
+            options = Physics.util.extend({}, defaults, options);
+
+            this.geometry = Physics.geometry('rectangle', {
+                width: options.width,
+                height: options.height
+            });
+
+            this.recalc();
+        },
+
+        // extended
+        recalc: function(){
+            var w = this.geometry.width;
+            var h = this.geometry.height;
+            parent.recalc.call(this);
+            // moment of inertia
+            this.moi = ( w*w + h*h ) * this.mass / 12;
+        }
+    };
+});
+
+
+// ---
+// inside: src/behaviors/attractor.js
+
+/** 
+ * class AttractorBehavior < Behavior
+ *
+ * `Physics.behavior('attractor')`.
+ *
+ * Attractor behavior attracts bodies to a specific point.
+ *
+ * Additional options include:
+ * - pos: The position of the attraction point
+ * - strength: How strong the attraction is (default: `1`)
+ * - order: The power of the inverse distance (default: `2` because that is newtonian gravity... inverse square)
+ * - max: The maximum distance in which to apply the attraction (default: Infinity)
+ * - min: The minimum distance above which to apply the attraction (default: very small non-zero)
+ **/
+Physics.behavior('attractor', function( parent ){
+
+    var defaults = {
+
+        pos: null, // default to (0, 0)
+        // how strong the attraction is
+        strength: 1,
+        // power of the inverse distance (2 is inverse square)
+        order: 2,
+        // max distance to apply it to
+        max: false, // infinite
+        // min distance to apply it to
+        min: false // auto calc
+    };
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            var self = this;
+            this._pos = new Physics.vector();
+            // call parent init method
+            parent.init.call( this );
+            this.options.defaults( defaults );
+            this.options.onChange(function( opts ){
+                self._maxDist = opts.max === false ? Infinity : opts.max;
+                self._minDist = opts.min ? opts.min : 10;
+                self.position( opts.pos );
+            });
+            this.options( options );
+        },
+
+        /**
+         * AttractorBehavior#position( [pos] ) -> this|Object
+         * - pos (Vectorish): The position to set
+         * + (Object): Returns the [[Vectorish]] position if no arguments provided
+         * + (this): For chaining
+         *
+         * Get or set the position of the attractor.
+         **/
+        position: function( pos ){
+            
+            var self = this;
+
+            if ( pos ){
+                this._pos.clone( pos );
+                return self;
+            }
+
+            return this._pos.values();
+        },
+        
+        // extended
+        behave: function( data ){
+
+            var bodies = this.getTargets()
+                ,body
+                ,order = this.options.order
+                ,strength = this.options.strength
+                ,minDist = this._minDist
+                ,maxDist = this._maxDist
+                ,scratch = Physics.scratchpad()
+                ,acc = scratch.vector()
+                ,norm
+                ,g
+                ;
+
+            for ( var j = 0, l = bodies.length; j < l; j++ ){
+                
+                body = bodies[ j ];
+
+                // clone the position
+                acc.clone( this._pos );
+                acc.vsub( body.state.pos );
+                // get the distance
+                norm = acc.norm();
+
+                if (norm > minDist && norm < maxDist){
+
+                    g = strength / Math.pow(norm, order);
+
+                    body.accelerate( acc.normalize().mult( g ) );
+                }
+            }
+
+            scratch.done();
+        }
+    };
+});
+
+
+// ---
+// inside: src/behaviors/body-collision-detection.js
+
+/**
+ * class BodyCollisionDetectionBehavior < Behavior
+ *
+ * `Physics.behavior('body-collision-detection')`.
+ *
+ * Detect collisions of bodies.
+ *
+ * Publishes collision events to the world as a group of detected collisions per iteration.
+ *
+ * The event data will have a `.collisions` property that is an array of collisions of the form:
+ *
+ * ```javascript
+ * {
+ *     bodyA: // the first body
+ *     bodyB: // the second body
+ *     norm: // the normal vector (Vectorish)
+ *     mtv: // the minimum transit vector. (the direction and length needed to extract bodyB from bodyA)
+ *     pos: // the collision point relative to bodyA
+ *     overlap: // the amount bodyA overlaps bodyB
+ * }
+ * ```
+ *
+ * Additional options include:
+ * - check: channel to listen to for collision candidates (default: `collisions:candidates`). set to `true` to force check every pair of bodies in the world
+ * - channel: channel to publish events to (default: `collisions:detected`)
+ **/
+Physics.behavior('body-collision-detection', function( parent ){
+
+    var supportFnStack = [];
+
+    /*
+     * getSupportFn( bodyA, bodyB ) -> Function
+     * - bodyA (Object): First body
+     * - bodyB (Object): Second body
+     * + (Function): The support function
+     *
+     * Get a general support function for use with GJK algorithm
+     */
+    var getSupportFn = function getSupportFn( bodyA, bodyB ){
+
+        var hash = Physics.util.pairHash( bodyA.uid, bodyB.uid )
+            ,fn = supportFnStack[ hash ]
+            ;
+
+        if ( !fn ){
+            fn = supportFnStack[ hash ] = function pairSupportFunction( searchDir ){
+
+                var tA = fn.tA
+                    ,tB = fn.tB
+                    ,vA = fn.tmpv1
+                    ,vB = fn.tmpv2
+                    ;
+
+                if ( fn.useCore ){
+                    vA = bodyA.geometry.getFarthestCorePoint( searchDir.rotateInv( tA ), vA, fn.marginA );
+                    vB = bodyB.geometry.getFarthestCorePoint( searchDir.rotate( tA ).rotateInv( tB ).negate(), vB, fn.marginB );
+                } else {
+                    vA = bodyA.geometry.getFarthestHullPoint( searchDir.rotateInv( tA ), vA );
+                    vB = bodyB.geometry.getFarthestHullPoint( searchDir.rotate( tA ).rotateInv( tB ).negate(), vB );
+                }
+
+                vA.vadd( bodyA.offset ).transform( tA );
+                vB.vadd( bodyB.offset ).transform( tB );
+                searchDir.negate().rotate( tB );
+
+                return {
+                    a: vA.values(),
+                    b: vB.values(),
+                    pt: vA.vsub( vB ).values()
+                };
+            };
+
+            // transforms for coordinate transformations
+            fn.tA = new Physics.transform();
+            fn.tB = new Physics.transform();
+
+            // temp vectors (used too frequently to justify scratchpad)
+            fn.tmpv1 = new Physics.vector();
+            fn.tmpv2 = new Physics.vector();
+        }
+
+        fn.useCore = false;
+        fn.margin = 0;
+        fn.tA.setRotation( bodyA.state.angular.pos ).setTranslation( bodyA.state.pos );
+        fn.tB.setRotation( bodyB.state.angular.pos ).setTranslation( bodyB.state.pos );
+        fn.bodyA = bodyA;
+        fn.bodyB = bodyB;
+
+        return fn;
+    };
+
+    /*
+     * checkGJK( bodyA, bodyB ) -> Object
+     * - bodyA (Object): First body
+     * - bodyB (Object): Second body
+     * + (Object): Collision result
+     *
+     * Use GJK algorithm to check arbitrary bodies for collisions
+     */
+    var checkGJK = function checkGJK( bodyA, bodyB ){
+
+        var scratch = Physics.scratchpad()
+            ,d = scratch.vector()
+            ,tmp = scratch.vector()
+            ,os = scratch.vector()
+            ,overlap
+            ,result
+            ,support
+            ,inc
+            ,collision = false
+            ,aabbA = bodyA.aabb()
+            ,dimA = Math.min( aabbA.hw, aabbA.hh )
+            ,aabbB = bodyB.aabb()
+            ,dimB = Math.min( aabbB.hw, aabbB.hh )
+            ;
+
+        // just check the overlap first
+        support = getSupportFn( bodyA, bodyB );
+        d.clone( bodyA.state.pos )
+            .vadd( bodyA.getGlobalOffset( os ) )
+            .vsub( bodyB.state.pos )
+            .vsub( bodyB.getGlobalOffset( os ) )
+            ;
+        result = Physics.gjk(support, d, true);
+
+        if ( result.overlap ){
+
+            // there is a collision. let's do more work.
+            collision = {
+                bodyA: bodyA,
+                bodyB: bodyB
+            };
+
+            // inc by 1% of the smallest dim.
+            inc = 1e-2 * Math.min(dimA || 1, dimB || 1);
+
+            // first get the min distance of between core objects
+            support.useCore = true;
+            support.marginA = 0;
+            support.marginB = 0;
+
+            // while there's still an overlap (or we don't have a positive distance)
+            // and the support margins aren't bigger than the shapes...
+            // search for the distance data
+            while ( (result.overlap || result.distance === 0) && (support.marginA < dimA || support.marginB < dimB) ){
+                if ( support.marginA < dimA ){
+                    support.marginA += inc;
+                }
+                if ( support.marginB < dimB ){
+                    support.marginB += inc;
+                }
+
+                result = Physics.gjk(support, d);
+            }
+
+            if ( result.overlap || result.maxIterationsReached ){
+                // This implementation can't deal with a core overlap yet
+                return scratch.done(false);
+            }
+
+            // calc overlap
+            overlap = (support.marginA + support.marginB) - result.distance;
+
+            if ( overlap <= 0 ){
+                return scratch.done(false);
+            }
+
+            collision.overlap = overlap;
+            // @TODO: for now, just let the normal be the mtv
+            collision.norm = d.clone( result.closest.b ).vsub( tmp.clone( result.closest.a ) ).normalize().values();
+            collision.mtv = d.mult( overlap ).values();
+            // get a corresponding hull point for one of the core points.. relative to body A
+            collision.pos = d.clone( collision.norm ).mult( support.marginA ).vadd( tmp.clone( result.closest.a ) ).vsub( bodyA.state.pos ).values();
+        }
+
+        return scratch.done( collision );
+    };
+
+    /*
+     * checkCircles( bodyA, bodyB ) -> Object
+     * - bodyA (Object): First body
+     * - bodyB (Object): Second body
+     * + (Object): Collision result
+     *
+     * Check two circles for collisions.
+     */
+    var checkCircles = function checkCircles( bodyA, bodyB ){
+
+        var scratch = Physics.scratchpad()
+            ,d = scratch.vector()
+            ,tmp = scratch.vector()
+            ,overlap
+            ,collision = false
+            ;
+
+        d.clone( bodyB.state.pos )
+            .vadd( bodyB.getGlobalOffset( tmp ) )
+            .vsub( bodyA.state.pos )
+            .vsub( bodyA.getGlobalOffset( tmp ) ) // save offset for later
+            ;
+        overlap = d.norm() - (bodyA.geometry.radius + bodyB.geometry.radius);
+
+        // hmm... they overlap exactly... choose a direction
+        if ( d.equals( Physics.vector.zero ) ){
+
+            d.set( 1, 0 );
+        }
+
+        if ( overlap <= 0 ){
+
+            collision = {
+                bodyA: bodyA,
+                bodyB: bodyB,
+                norm: d.normalize().values(),
+                mtv: d.mult( -overlap ).values(),
+                pos: d.mult( -bodyA.geometry.radius/overlap ).vadd( tmp ).values(),
+                overlap: -overlap
+            };
+        }
+
+        return scratch.done( collision );
+    };
+
+    /*
+     * checkPair( bodyA, bodyB[, disp] ) -> Object
+     * - bodyA (Object): First body
+     * - bodyB (Object): Second body
+     * + (Object): Collision result
+     *
+     * Check a pair for collisions
+     */
+    var checkPair = function checkPair( bodyA, bodyB ){
+
+        // filter out bodies that don't collide with each other
+        if (
+            ( bodyA.treatment === 'static' || bodyA.treatment === 'kinematic' ) &&
+            ( bodyB.treatment === 'static' || bodyB.treatment === 'kinematic' )
+        ){
+            return false;
+        }
+
+        if ( bodyA.geometry.name === 'circle' && bodyB.geometry.name === 'circle' ){
+
+            return checkCircles( bodyA, bodyB );
+
+        } else if ( bodyA.geometry.name === 'compound' || bodyB.geometry.name === 'compound' ){
+            // compound bodies are special. We can't use gjk because
+            // they could have concavities. so we do the pieces individually
+            var test = (bodyA.geometry.name === 'compound')
+                ,compound = test ? bodyA : bodyB
+                ,other = test ? bodyB : bodyA
+                ,cols
+                ,ch
+                ,ret = []
+                ,scratch = Physics.scratchpad()
+                ,vec = scratch.vector()
+                ,oldPos = scratch.vector()
+                ,otherAABB = other.aabb()
+                ,i
+                ,l
+                ;
+
+            for ( i = 0, l = compound.children.length; i < l; i++ ){
+
+                ch = compound.children[ i ];
+                // move body to fake position
+                oldPos.clone( ch.state.pos );
+                ch.offset.vadd( oldPos.vadd( compound.offset ).rotate( -ch.state.angular.pos ) );
+                ch.state.pos.clone( compound.state.pos );
+                ch.state.angular.pos += compound.state.angular.pos;
+
+                // check it if the aabbs overlap
+                if ( Physics.aabb.overlap(otherAABB, ch.aabb()) ){
+
+                    cols = checkPair( other, ch );
+
+                    if ( cols instanceof Array ){
+                        for ( var j = 0, c, ll = cols.length; j < ll; j++ ){
+                            c = cols[j];
+                            // set body to be the compound body
+                            if ( c.bodyA === ch ){
+                                c.bodyA = compound;
+                            } else {
+                                c.bodyB = compound;
+                            }
+                            ret.push( c );
+                        }
+
+                    } else if ( cols ) {
+                        // set body to be the compound body
+                        if ( cols.bodyA === ch ){
+                            cols.bodyA = compound;
+                        } else {
+                            cols.bodyB = compound;
+                        }
+                        ret.push( cols );
+                    }
+                }
+
+                // transform it back
+                ch.state.angular.pos -= compound.state.angular.pos;
+                ch.offset.vsub( oldPos );
+                ch.state.pos.clone( oldPos.rotate( ch.state.angular.pos ).vsub( compound.offset ) );
+            }
+
+            return scratch.done( ret );
+
+        } else {
+
+            return checkGJK( bodyA, bodyB );
+        }
+    };
+
+    var defaults = {
+
+        // channel to listen to for collision candidates
+        // set to "true" to force check every pair of bodies in the world
+        check: 'collisions:candidates',
+
+        // channel to publish events to
+        channel: 'collisions:detected'
+    };
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            parent.init.call( this );
+            this.options.defaults( defaults );
+            this.options( options );
+        },
+
+        // extended
+        connect: function( world ){
+
+            if ( this.options.check === true ){
+
+                world.on( 'integrate:velocities', this.checkAll, this );
+
+            } else {
+
+                world.on( this.options.check, this.check, this );
+            }
+        },
+
+        // extended
+        disconnect: function( world ){
+
+            if ( this.options.check === true ){
+
+                world.off( 'integrate:velocities', this.checkAll, this );
+
+            } else {
+
+                world.off( this.options.check, this.check, this );
+            }
+        },
+
+        /** internal
+         * BodyCollisionDetectionBehavior#check( data )
+         * - data (Object): The event data
+         *
+         * Event callback to check pairs of objects that have been flagged by broad phase for possible collisions.
+         **/
+        check: function( data ){
+
+            var candidates = data.candidates
+                ,pair
+                ,targets = this.getTargets()
+                ,collisions = []
+                ,ret
+                ,prevContacts = this.prevContacts || {}
+                ,contactList = {}
+                ,pairHash = Physics.util.pairHash
+                ,hash
+                ;
+
+            for ( var i = 0, l = candidates.length; i < l; ++i ){
+
+                pair = candidates[ i ];
+
+                if ( targets === this._world._bodies ||
+                    // only check if the members are targeted by this behavior
+                    (Physics.util.indexOf( targets, pair.bodyA ) > -1) &&
+                    (Physics.util.indexOf( targets, pair.bodyB ) > -1)
+                ){
+                    ret = checkPair( pair.bodyA, pair.bodyB );
+
+                    if ( ret instanceof Array ){
+
+                        for ( var j = 0, r, ll = ret.length; j < ll; j++ ){
+                            r = ret[j];
+                            if ( r ){
+                                hash = pairHash( pair.bodyA.uid, pair.bodyB.uid );
+                                contactList[ hash ] = true;
+                                r.collidedPreviously = prevContacts[ hash ];
+                                collisions.push( r );
+                            }
+                        }
+
+                    } else if ( ret ){
+                        hash = pairHash( pair.bodyA.uid, pair.bodyB.uid );
+                        contactList[ hash ] = true;
+                        ret.collidedPreviously = prevContacts[ hash ];
+
+                        collisions.push( ret );
+                    }
+                }
+            }
+
+            this.prevContacts = contactList;
+
+            if ( collisions.length ){
+
+                this._world.emit( this.options.channel, {
+                    collisions: collisions
+                });
+            }
+        },
+
+        /** internal
+         * BodyCollisionDetectionBehavior#checkAll( data )
+         * - data (Object): The event data
+         *
+         * Event callback to check all pairs of objects in the list for collisions
+         **/
+        checkAll: function( data ){
+
+            var bodies = this.getTargets()
+                ,dt = data.dt
+                ,bodyA
+                ,bodyB
+                ,collisions = []
+                ,ret
+                ,prevContacts = this.prevContacts || {}
+                ,contactList = {}
+                ,pairHash = Physics.util.pairHash
+                ,hash
+                ;
+
+            for ( var j = 0, l = bodies.length; j < l; j++ ){
+
+                bodyA = bodies[ j ];
+
+                for ( var i = j + 1; i < l; i++ ){
+
+                    bodyB = bodies[ i ];
+
+                    ret = checkPair( bodyA, bodyB );
+
+                    if ( ret instanceof Array ){
+
+                        for ( var k = 0, r, ll = ret.length; k < ll; k++ ){
+                            r = ret[k];
+                            if ( r ){
+                                hash = pairHash( bodyA.uid, bodyB.uid );
+                                contactList[ hash ] = true;
+                                r.collidedPreviously = prevContacts[ hash ];
+                                collisions.push( r );
+                            }
+                        }
+
+                    } else if ( ret ){
+                        hash = pairHash( bodyA.uid, bodyB.uid );
+                        contactList[ hash ] = true;
+                        ret.collidedPreviously = prevContacts[ hash ];
+
+                        collisions.push( ret );
+                    }
+                }
+            }
+
+            this.prevContacts = contactList;
+
+            if ( collisions.length ){
+
+                this._world.emit( this.options.channel, {
+                    collisions: collisions
+                });
+            }
+        }
+    };
+
+});
+
+
+// ---
+// inside: src/behaviors/body-impulse-response.js
+
+/**
+ * class BodyImpulseResponseBehavior < Behavior
+ *
+ * `Physics.behavior('body-impulse-response')`.
+ *
+ * Responds to collisions by applying impulses.
+ *
+ * Additional options include:
+ * - check: channel to listen to for collisions (default: `collisions:detected`).
+ * - mtvThreshold: apply partial extraction of bodies if the minimum transit vector is less than this value ( default: `1`)
+ *   this will depend on your simulation characteristic length scale
+ * - bodyExtractDropoff: every body overlap correction (underneith mtvThreshold) will only extract by this fraction (0..1). Helps with stablizing contacts. (default: `0.5`)
+ * - forceWakeupAboveOverlapThreshold: force bodies to wake up if the overlap is above mtvThreshold ( default: `true` )
+ **/
+Physics.behavior('body-impulse-response', function( parent ){
+
+    var defaults = {
+        // channel to listen to for collisions
+        check: 'collisions:detected'
+        // apply partial extraction of bodies if the minimum transit vector is less than this value
+        // this will depend on your simulation characteristic length scale
+        ,mtvThreshold: 1
+        // every body overlap correction (underneith mtvThreshold) will only extract by this fraction (0..1)
+        // helps with stablizing contacts.
+        ,bodyExtractDropoff: 0.5
+        // force bodies to wake up if the overlap is above mtvThreshold
+        ,forceWakeupAboveOverlapThreshold: true
+    };
+
+    function getUid( b ){
+        return b.uid;
+    }
+
+    function clampMTV( totalV, mtv, into ){
+
+        var m, n;
+        n = mtv.norm();
+        m = n - totalV.proj( mtv );
+        m = Math.max( 0, Math.min( n, m ) );
+
+        if ( n === 0 ){
+            into.zero();
+        } else {
+            into.clone( mtv ).mult( m/n );
+        }
+
+        return into;
+    }
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            parent.init.call( this );
+            this.options.defaults( defaults );
+            this.options( options );
+
+            this._bodyList = [];
+        },
+
+        // no applyTo method
+        applyTo: false,
+
+        // extended
+        connect: function( world ){
+
+            world.on( this.options.check, this.respond, this );
+        },
+
+        // extended
+        disconnect: function( world ){
+
+            world.off( this.options.check, this.respond, this );
+        },
+
+        /** internal
+         * BodyImpulseResponseBehavior#collideBodes( bodyA, bodyB, normal, point, mtrans, contact )
+         * - bodyA (Object): First Body
+         * - bodyB (Object): Second body
+         * - normal (Vector): Normal vector of the collision surface
+         * - point (Vector): Contact point of the collision
+         * - mtrans (Vector): Minimum transit vector that is the smallest displacement to separate the bodies
+         * - contact (Boolean): Are the bodies in resting contact relative to each other
+         *
+         * Collide two bodies by modifying their positions and velocities to conserve momentum
+         **/
+        collideBodies: function(bodyA, bodyB, normal, point, mtrans, contact){
+
+            var fixedA = bodyA.treatment === 'static' || bodyA.treatment === 'kinematic'
+                ,fixedB = bodyB.treatment === 'static' || bodyB.treatment === 'kinematic'
+                ,scratch = Physics.scratchpad()
+                // minimum transit vector for each body
+                ,mtv = scratch.vector().clone( mtrans )
+                ;
+
+            // do nothing if both are fixed
+            if ( fixedA && fixedB ){
+                scratch.done();
+                return;
+            }
+
+            // inverse masses and moments of inertia.
+            // give fixed bodies infinite mass and moi
+            var invMoiA = fixedA ? 0 : 1 / bodyA.moi
+                ,invMoiB = fixedB ? 0 : 1 / bodyB.moi
+                ,invMassA = fixedA ? 0 : 1 / bodyA.mass
+                ,invMassB = fixedB ? 0 : 1 / bodyB.mass
+                // coefficient of restitution between bodies
+                ,cor = bodyA.restitution * bodyB.restitution
+                // coefficient of friction between bodies
+                ,cof = bodyA.cof * bodyB.cof
+                // normal vector
+                ,n = scratch.vector().clone( normal )
+                // vector perpendicular to n
+                ,perp = scratch.vector().clone( n ).perp()
+                ,tmp = scratch.vector()
+                // collision point from A's center
+                ,rA = scratch.vector().clone( point )
+                // collision point from B's center
+                ,rB = scratch.vector().clone( point )
+                    .vadd( bodyA.state.pos )
+                    .vsub( bodyB.state.pos )
+                ,angVelA = bodyA.state.angular.vel
+                ,angVelB = bodyB.state.angular.vel
+                // relative velocity towards B at collision point
+                ,vAB = scratch.vector().clone( bodyB.state.vel )
+                        .vadd( tmp.clone(rB).perp().mult( angVelB ) )
+                        .vsub( bodyA.state.vel )
+                        .vsub( tmp.clone(rA).perp().mult( angVelA ) )
+                // break up components along normal and perp-normal directions
+                ,rAproj = rA.proj( n )
+                ,rAreg = rA.proj( perp )
+                ,rBproj = rB.proj( n )
+                ,rBreg = rB.proj( perp )
+                ,vproj = vAB.proj( n ) // projection of vAB along n
+                ,vreg = vAB.proj( perp ) // rejection of vAB along n (perp of proj)
+                ,impulse
+                ,sign
+                ,max
+                ,ratio
+                ,inContact = contact
+                ;
+
+            if ( contact ){
+
+                if ( fixedA ){
+
+                    clampMTV( bodyB._mtvTotal, mtv, tmp );
+                    bodyB._mtvTotal.vadd( tmp );
+
+                } else if ( fixedB ){
+
+                    clampMTV( bodyA._mtvTotal, mtv.negate(), tmp );
+                    bodyA._mtvTotal.vadd( tmp );
+                    mtv.negate();
+
+                } else {
+
+                    ratio = 0.5; //bodyA.mass / ( bodyA.mass + bodyB.mass );
+                    mtv.mult( ratio );
+                    clampMTV( bodyB._mtvTotal, mtv, tmp );
+                    bodyB._mtvTotal.vadd( tmp );
+
+                    mtv.clone( mtrans ).mult( ratio - 1 );
+                    clampMTV( bodyA._mtvTotal, mtv, tmp );
+                    bodyA._mtvTotal.vadd( tmp );
+
+                }
+            }
+
+            // if moving away from each other... don't bother.
+            if (vproj >= 0){
+                scratch.done();
+                return;
+            }
+
+            invMoiA = invMoiA === Infinity ? 0 : invMoiA;
+            invMoiB = invMoiB === Infinity ? 0 : invMoiB;
+
+            impulse =  - ((1 + cor) * vproj) / ( invMassA + invMassB + (invMoiA * rAreg * rAreg) + (invMoiB * rBreg * rBreg) );
+            // vproj += impulse * ( invMass + (invMoi * rreg * rreg) );
+            // angVel -= impulse * rreg * invMoi;
+
+
+            if ( fixedA ){
+
+                // apply impulse
+                bodyB.state.vel.vadd( n.mult( impulse * invMassB ) );
+                bodyB.state.angular.vel -= impulse * invMoiB * rBreg;
+
+            } else if ( fixedB ){
+
+                // apply impulse
+                bodyA.state.vel.vsub( n.mult( impulse * invMassA ) );
+                bodyA.state.angular.vel += impulse * invMoiA * rAreg;
+
+            } else {
+
+                // apply impulse
+                bodyB.state.vel.vadd( n.mult( impulse * invMassB ) );
+                bodyB.state.angular.vel -= impulse * invMoiB * rBreg;
+                bodyA.state.vel.vsub( n.mult( invMassA * bodyB.mass ) );
+                bodyA.state.angular.vel += impulse * invMoiA * rAreg;
+            }
+
+            // inContact = (impulse < 0.004);
+
+            // if we have friction and a relative velocity perpendicular to the normal
+            if ( cof && vreg ){
+
+
+                // TODO: here, we could first assume static friction applies
+                // and that the tangential relative velocity is zero.
+                // Then we could calculate the impulse and check if the
+                // tangential impulse is less than that allowed by static
+                // friction. If not, _then_ apply kinetic friction.
+
+                // instead we're just applying kinetic friction and making
+                // sure the impulse we apply is less than the maximum
+                // allowed amount
+
+                // maximum impulse allowed by kinetic friction
+                max = Math.abs(vreg) / ( invMassA + invMassB + (invMoiA * rAproj * rAproj) + (invMoiB * rBproj * rBproj) );
+                // the sign of vreg ( plus or minus 1 )
+                sign = vreg < 0 ? -1 : 1;
+
+                // get impulse due to friction
+                impulse = cof * Math.abs( impulse );
+                // constrain the impulse within the "friction cone" ( max < mu * impulse)
+                impulse = Math.min( impulse, max );
+                impulse *= sign;
+
+                if ( fixedA ){
+
+                    // apply frictional impulse
+                    bodyB.state.vel.vsub( perp.mult( impulse * invMassB ) );
+                    bodyB.state.angular.vel -= impulse * invMoiB * rBproj;
+
+                } else if ( fixedB ){
+
+                    // apply frictional impulse
+                    bodyA.state.vel.vadd( perp.mult( impulse * invMassA ) );
+                    bodyA.state.angular.vel += impulse * invMoiA * rAproj;
+
+                } else {
+
+                    // apply frictional impulse
+                    bodyB.state.vel.vsub( perp.mult( impulse * invMassB ) );
+                    bodyB.state.angular.vel -= impulse * invMoiB * rBproj;
+                    bodyA.state.vel.vadd( perp.mult( invMassA * bodyB.mass ) );
+                    bodyA.state.angular.vel += impulse * invMoiA * rAproj;
+                }
+            }
+
+            // wake up bodies if necessary
+            if ( bodyA.sleep() ){
+                bodyA.sleepCheck();
+            }
+            if ( bodyB.sleep() ){
+                bodyB.sleepCheck();
+            }
+
+            scratch.done();
+        },
+
+        // internal
+        _pushUniq: function( body ){
+            var idx = Physics.util.sortedIndex( this._bodyList, body, getUid );
+            if ( this._bodyList[ idx ] !== body ){
+                this._bodyList.splice( idx, 0, body );
+            }
+        },
+
+        /** internal
+         * BodyImpulseResponseBehavior#respond( data )
+         * - data (Object): event data
+         *
+         * Event callback to respond to collision data.
+         **/
+        respond: function( data ){
+
+            var self = this
+                ,col
+                ,collisions = data.collisions// Physics.util.shuffle(data.collisions)
+                ,i,l,b
+                ;
+
+            for ( i = 0, l = collisions.length; i < l; ++i ){
+
+                col = collisions[ i ];
+                // add bodies to list for later
+                this._pushUniq( col.bodyA );
+                this._pushUniq( col.bodyB );
+                // ensure they have mtv stat vectors
+                col.bodyA._mtvTotal = col.bodyA._mtvTotal || new Physics.vector();
+                col.bodyB._mtvTotal = col.bodyB._mtvTotal || new Physics.vector();
+                col.bodyA._oldmtvTotal = col.bodyA._oldmtvTotal || new Physics.vector();
+                col.bodyB._oldmtvTotal = col.bodyB._oldmtvTotal || new Physics.vector();
+
+                self.collideBodies(
+                    col.bodyA,
+                    col.bodyB,
+                    col.norm,
+                    col.pos,
+                    col.mtv,
+                    col.collidedPreviously
+                );
+            }
+
+            // apply mtv vectors from the average mtv vector
+            for ( i = 0, l = this._bodyList.length; i < l; ++i ){
+                b = this._bodyList.pop();
+                // clampMTV( b._oldmtvTotal, b._mtvTotal, b._mtvTotal );
+
+                if ( b._mtvTotal.normSq() < this.options.mtvThreshold ){
+                    b._mtvTotal.mult( this.options.bodyExtractDropoff );
+                } else if ( this.options.forceWakeupAboveOverlapThreshold ) {
+                    // wake up bodies if necessary
+                    b.sleep( false );
+                }
+
+                b.state.pos.vadd( b._mtvTotal );
+                b.state.old.pos.vadd( b._mtvTotal );
+                b._oldmtvTotal.swap( b._mtvTotal );
+                b._mtvTotal.zero();
+            }
+        }
+    };
+});
+
+
+// ---
+// inside: src/behaviors/constant-acceleration.js
+
+/** 
+ * class ConstantAccelerationBehavior < Behavior
+ *
+ * `Physics.behavior('constant-acceleration')`.
+ *
+ * Constant acceleration behavior.
+ *
+ * Basically the "gravity" behavior. Used to give "earth-like gravity" to the world.
+ *
+ * Additional options include:
+ * - acc: The acceleration vector (Vectorish). (default: `{ x: 0, y: 0.0004 }`)
+ **/
+Physics.behavior('constant-acceleration', function( parent ){
+
+    var defaults = {
+
+        acc: { x : 0, y: 0.0004 }
+    };
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            parent.init.call( this );
+            this.options.defaults( defaults );
+            this.options( options );
+
+            // extend options
+            this._acc = new Physics.vector();
+            this.setAcceleration( this.options.acc );
+            delete this.options.acc;
+        },
+
+        /**
+         * ConstantAccelerationBehavior#setAcceleration( acc ) -> this
+         * - acc (Vectorish): The acceleration vector
+         * 
+         * Set the acceleration of the behavior.
+         **/
+        setAcceleration: function( acc ){
+
+            this._acc.clone( acc );
+            return this;
+        },
+
+        // extended
+        behave: function( data ){
+
+            var bodies = this.getTargets();
+
+            for ( var i = 0, l = bodies.length; i < l; ++i ){
+                
+                bodies[ i ].accelerate( this._acc );
+            }
+        }
+    };
+});
+
+// ---
+// inside: src/behaviors/edge-collision-detection.js
+
+/**
+ * class EdgeCollisionDetectionBehavior < Behavior
+ *
+ * `Physics.behavior('edge-collision-detection')`.
+ *
+ * Used to detect collisions with the boundaries of an AABB.
+ *
+ * Additional options include:
+ * - aabb: The [[Physics.aabb]] bounds to use as the constraining boundary
+ * - restitution: The restitution of the boundary walls (default: `0.99`)
+ * - cof: The coefficient of friction of the boundary walls (default: `1`)
+ * - channel: The channel to publish collisions to. (default: 'collisions:detected')
+ **/
+Physics.behavior('edge-collision-detection', function( parent ){
+
+    /*
+     * checkGeneral( body, bounds, dummy ) -> Array
+     * - body (Body): The body to check
+     * - bounds (Physics.aabb): The boundary
+     * - dummy: (Body): The dummy body to publish as the static other body it collides with
+     * + (Array): The collision data
+     *
+     * Check if a body collides with the boundary
+     */
+    var checkGeneral = function checkGeneral( body, bounds, dummy ){
+
+        var overlap
+            ,aabb = body.aabb()
+            ,scratch = Physics.scratchpad()
+            ,offset = body.getGlobalOffset( scratch.vector() )
+            ,trans = scratch.transform()
+            ,dir = scratch.vector()
+            ,result = scratch.vector()
+            ,collision = false
+            ,collisions = []
+            ;
+
+        // right
+        overlap = (aabb.x + aabb.hw) - bounds.max.x;
+
+        if ( overlap >= 0 ){
+
+            dir.set( 1, 0 ).rotateInv( trans.setRotation( body.state.angular.pos ) );
+
+            collision = {
+                bodyA: body,
+                bodyB: dummy,
+                overlap: overlap,
+                norm: {
+                    x: 1,
+                    y: 0
+                },
+                mtv: {
+                    x: overlap,
+                    y: 0
+                },
+                pos: body.geometry.getFarthestHullPoint( dir, result ).rotate( trans ).vadd( offset ).values()
+            };
+
+            collisions.push(collision);
+        }
+
+        // bottom
+        overlap = (aabb.y + aabb.hh) - bounds.max.y;
+
+        if ( overlap >= 0 ){
+
+            dir.set( 0, 1 ).rotateInv( trans.setRotation( body.state.angular.pos ) );
+
+            collision = {
+                bodyA: body,
+                bodyB: dummy,
+                overlap: overlap,
+                norm: {
+                    x: 0,
+                    y: 1
+                },
+                mtv: {
+                    x: 0,
+                    y: overlap
+                },
+                pos: body.geometry.getFarthestHullPoint( dir, result ).rotate( trans ).vadd( offset ).values()
+            };
+
+            collisions.push(collision);
+        }
+
+        // left
+        overlap = bounds.min.x - (aabb.x - aabb.hw);
+
+        if ( overlap >= 0 ){
+
+            dir.set( -1, 0 ).rotateInv( trans.setRotation( body.state.angular.pos ) );
+
+            collision = {
+                bodyA: body,
+                bodyB: dummy,
+                overlap: overlap,
+                norm: {
+                    x: -1,
+                    y: 0
+                },
+                mtv: {
+                    x: -overlap,
+                    y: 0
+                },
+                pos: body.geometry.getFarthestHullPoint( dir, result ).rotate( trans ).vadd( offset ).values()
+            };
+
+            collisions.push(collision);
+        }
+
+        // top
+        overlap = bounds.min.y - (aabb.y - aabb.hh);
+
+        if ( overlap >= 0 ){
+
+            dir.set( 0, -1 ).rotateInv( trans.setRotation( body.state.angular.pos ) );
+
+            collision = {
+                bodyA: body,
+                bodyB: dummy,
+                overlap: overlap,
+                norm: {
+                    x: 0,
+                    y: -1
+                },
+                mtv: {
+                    x: 0,
+                    y: -overlap
+                },
+                pos: body.geometry.getFarthestHullPoint( dir, result ).rotate( trans ).vadd( offset ).values()
+            };
+
+            collisions.push(collision);
+        }
+
+        scratch.done();
+        return collisions;
+    };
+
+    /*
+     * checkEdgeCollide( body, bounds, dummy ) -> Array
+     * - body (Body): The body to check
+     * - bounds (Physics.aabb): The boundary
+     * - dummy: (Body): The dummy body to publish as the static other body it collides with
+     * + (Array): The collision data
+     *
+     * Check if a body collides with the boundary
+     */
+    var checkEdgeCollide = function checkEdgeCollide( body, bounds, dummy ){
+
+        return checkGeneral( body, bounds, dummy );
+    };
+
+    var defaults = {
+
+        aabb: null,
+        restitution: 0.99,
+        cof: 1.0,
+        channel: 'collisions:detected'
+    };
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            parent.init.call( this );
+            this.options.defaults( defaults );
+            this.options( options );
+
+            this.setAABB( this.options.aabb );
+            this.restitution = this.options.restitution;
+
+            this.body = Physics.body('point', {
+                treatment: 'static',
+                restitution: this.options.restitution,
+                cof: this.options.cof
+            });
+        },
+
+        /**
+         * EdgeCollisionDetectionBehavior#setAABB( aabb ) -> this
+         * - aabb (Physics.aabb): The aabb to use as the boundary
+         *
+         * Set the boundaries of the edge.
+         **/
+        setAABB: function( aabb ){
+
+            if (!aabb) {
+                throw 'Error: aabb not set';
+            }
+
+            this._edges = {
+                min: {
+                    x: (aabb.x - aabb.hw),
+                    y: (aabb.y - aabb.hh)
+                },
+                max: {
+                    x: (aabb.x + aabb.hw),
+                    y: (aabb.y + aabb.hh)
+                }
+            };
+
+            return this;
+        },
+
+        // extended
+        connect: function( world ){
+
+            world.on( 'integrate:positions', this.checkAll, this, 2 );
+        },
+
+        // extended
+        disconnect: function( world ){
+
+            world.off( 'integrate:positions', this.checkAll, this, 2 );
+        },
+
+        /** internal
+         * EdgeCollisionDetectionBehavior#checkAll( data )
+         * - data (Object): Event data
+         *
+         * Event callback to check all bodies for collisions with the edge
+         **/
+        checkAll: function( data ){
+
+            var bodies = this.getTargets()
+                ,dt = data.dt
+                ,body
+                ,collisions = []
+                ,ret
+                ,bounds = this._edges
+                ,dummy = this.body
+                ,prevContacts = this.prevContacts || {}
+                ,contactList = {}
+                ,pairHash = Physics.util.pairHash
+                ,hash
+                ;
+
+            for ( var i = 0, l = bodies.length; i < l; i++ ){
+
+                body = bodies[ i ];
+
+                // only detect dynamic bodies
+                if ( body.treatment === 'dynamic' ){
+
+                    ret = checkEdgeCollide( body, bounds, dummy );
+
+                    if ( ret ){
+                        hash = pairHash( body.uid, dummy.uid );
+
+                        for ( var j = 0, ll = ret.length; j < ll; j++ ){
+                            contactList[ hash ] = true;
+                            ret[ j ].collidedPreviously = prevContacts[ hash ];
+                        }
+
+                        collisions.push.apply( collisions, ret );
+                    }
+                }
+            }
+
+            this.prevContacts = contactList;
+
+            if ( collisions.length ){
+
+                this._world.emit( this.options.channel, {
+                    collisions: collisions
+                });
+            }
+        }
+    };
+
+});
+
+
+// ---
+// inside: src/behaviors/interactive.js
+
+/**
+ * class InteractiveBehavior < Behavior
+ *
+ * `Physics.behavior('interactive')`.
+ *
+ * User interaction helper.
+ *
+ * Used to get mouse/touch events and add grab interactions.
+ *
+ * Additional options include:
+ * - el: The element of the renderer. What you input as the `el` for the renderer.
+ * - moveThrottle: The min time between move events (default: `10`).
+ * - minVel: The minimum velocity clamp [[Vectorish]] (default: { x: -5, y: -5 }) to restrict velocity a user can give to a body
+ * - maxVel: The maximum velocity clamp [[Vectorish]] (default: { x: 5, y: 5 }) to restrict velocity a user can give to a body
+ *
+ * The behavior also triggers the following events on the world:
+ * ```javascript
+ * // a body has been grabbed
+ * world.on('interact:grab', function( data ){
+ *     data.x; // the x coord
+ *     data.y; // the y coord
+ *     data.body; // the body that was grabbed
+ * });
+ * // no body was grabbed, but the renderer area was clicked, or touched
+ * world.on('interact:poke', function( data ){
+ *     data.x; // the x coord
+ *     data.y; // the y coord
+ * });
+ * // when a mouse or pointer moves
+ * world.on('interact:move', function( data ){
+ *     data.x; // the x coord
+ *     data.y; // the y coord
+ *     data.body; // the grabbed body that was moved (if applicable)
+ * });
+ * // when the viewport is released (mouseup, touchend)
+ * world.on('interact:release', function( data ){
+ *     data.x; // the x coord
+ *     data.y; // the y coord
+ *     data.body; // the body that was grabbed (if applicable)
+ * });
+ * ```
+ *
+ * The behavior also sets body.isGrabbed = true for any grabbed bodies while they are grabbed.
+ **/
+Physics.behavior('interactive', function( parent ){
+
+    if ( !document ){
+        // must be in node environment
+        return {};
+    }
+
+    var defaults = {
+            // the element to monitor
+            el: null,
+            // time between move events
+            moveThrottle: 1000 / 100 | 0,
+            // minimum velocity clamp
+            minVel: { x: -5, y: -5 },
+            // maximum velocity clamp
+            maxVel: { x: 5, y: 5 }
+        }
+        ,getElementOffset = function( el ){
+            var curleft = 0
+                ,curtop = 0
+                ;
+
+            if (el.offsetParent) {
+                do {
+                    curleft += el.offsetLeft;
+                    curtop += el.offsetTop;
+                } while (el = el.offsetParent);
+            }
+
+            return { left: curleft, top: curtop };
+        }
+        ;
+
+    return {
+        // extended
+        init: function( options ){
+
+            var self = this;
+
+            // call parent init method
+            parent.init.call( this );
+            this.options.defaults( defaults );
+            this.options( options );
+
+            // vars
+            this.bodyData = {};
+            this.bodyDataByUID = {};
+
+            this.el = typeof this.options.el === 'string' ? document.getElementById(this.options.el) : this.options.el;
+
+            if ( !this.el ){
+                throw "No DOM element specified";
+            }
+
+            // init events
+            // when there are multiple touchdowns, grab is usually called separately for each,
+            // but we loop through e.changedTouches just in case
+            self.grab = function grab( e ){
+                var pos
+                    ,body
+                    ,touchId
+                    ,touch
+                    ,offset
+                    ,data
+                    ,touchIndex
+                    ,l
+                    ;
+
+                if ( self._world ){
+
+                    // Adjust for PointerEvent and older browsers
+                    if ( !e.changedTouches ) {
+                        e.changedTouches = [ e ];
+                    }
+
+                    offset = getElementOffset( e.target );
+
+                    for ( touchIndex = 0, l = e.changedTouches.length; touchIndex < l; touchIndex++) {
+                        touch = e.changedTouches[touchIndex];
+                        touchId = touch.identifier || touch.pointerId || "mouse";
+                        pos = { idx: touchId, x: touch.pageX - offset.left, y: touch.pageY - offset.top };
+                        body = self._world.findOne({ $at: new Physics.vector( pos ), $in: self.getTargets() });
+
+                        if ( body ){
+                            // we're trying to grab a body
+
+                            // fix the body in place
+                            body.state.vel.zero();
+                            body.state.angular.vel = 0;
+                            body.isGrabbed = true;
+                            // remember the currently grabbed bodies
+                            data = self.bodyData[touchId] || {};
+                            data.body = body;
+                            // wake the body up
+                            body.sleep( false );
+                            data.time = Physics.util.ticker.now();
+
+                            // if we're grabbing the same body twice we don't want to remember the wrong treatment.
+                            data.treatment = self.bodyDataByUID[ body.uid ] ? self.bodyDataByUID[ body.uid ].treatment : body.treatment;
+                            // change its treatment but remember its old treatment
+                            body.treatment = 'kinematic';
+                            // remember the click/touch offset
+                            data.pos = data.pos || new Physics.vector();
+                            data.pos.clone( pos );
+
+                            data.offset = data.offset || new Physics.vector();
+                            data.offset.clone( pos ).vsub( body.state.pos );
+                            // init touchPointsOld here, too, so we don't have to do it in "move"
+                            data.oldPos = data.oldPos || new Physics.vector();
+                            data.oldPos.clone( pos );
+
+                            pos.body = body;
+                            self.bodyData[touchId] = data;
+                            self.bodyDataByUID[ body.uid ] = data;
+                            self._world.emit('interact:grab', pos);
+
+                        } else {
+
+                            self._world.emit('interact:poke', pos);
+                        }
+                    }
+                }
+            };
+
+            // when there are multiple touchdowns, move is called once
+            // and e.changedTouches will have one or more touches in it
+            self.move = Physics.util.throttle(function move( e ){
+                var pos
+                    ,state
+                    ,body
+                    ,touchId
+                    ,touch
+                    ,offset
+                    ,data
+                    ,touchIndex
+                    ,l
+                    ;
+
+                if ( self._world ){
+
+                    // Adjust for PointerEvent and older browsers
+                    if ( !e.changedTouches ) {
+                        e.changedTouches = [ e ];
+                    }
+
+                    offset = getElementOffset( self.el );
+
+                    for ( touchIndex = 0, l = e.changedTouches.length; touchIndex < l; touchIndex++) {
+                        touch = e.changedTouches[touchIndex];
+                        touchId = touch.identifier || touch.pointerId || "mouse";
+                        pos = { idx: touchId, x: touch.pageX - offset.left, y: touch.pageY - offset.top };
+                        data = self.bodyData[touchId];
+
+                        if ( data ){
+                            body = data.body;
+
+                            // wake the body up
+                            body.sleep( false );
+                            data.time = Physics.util.ticker.now();
+
+                            // set old mouse position
+                            data.oldPos.clone( data.pos );
+                            // get new mouse position
+                            data.pos.clone( pos );
+
+                            pos.body = body;
+                        }
+
+                        self._world.emit('interact:move', pos);
+                    }
+                }
+
+            }, self.options.moveThrottle);
+
+            // when there are multiple touchups, release is called once
+            // and e.changedTouches will have one or more touches in it
+            self.release = function release( e ){
+                var pos
+                    ,body
+                    ,touchId
+                    ,touch
+                    ,offset
+                    ,data
+                    ,dt
+                    ,touchIndex
+                    ,l
+                    ;
+
+                if ( self._world ){
+
+                    // Adjust for PointerEvent and older browsers
+                    if ( !e.changedTouches ) {
+                        e.changedTouches = [ e ];
+                    }
+
+                    for ( touchIndex = 0, l = e.changedTouches.length; touchIndex < l; touchIndex++) {
+                        offset = getElementOffset( self.el );
+                        touch = e.changedTouches[touchIndex];
+                        touchId = touch.identifier || touch.pointerId || "mouse";
+                        pos = { idx: touchId, x: touch.pageX - offset.left, y: touch.pageY - offset.top };
+                        data = self.bodyData[touchId];
+
+                        // release the body
+                        if ( data ){
+                            body = data.body;
+                            // wake the body up
+                            body.sleep( false );
+                            // get new mouse position
+                            data.pos.clone( pos );
+
+                            dt = Math.max(Physics.util.ticker.now() - data.time, self.options.moveThrottle);
+                            body.treatment = data.treatment;
+                            // calculate the release velocity
+                            body.state.vel.clone( data.pos ).vsub( data.oldPos ).mult( 1 / dt );
+                            // make sure it's not too big
+                            body.state.vel.clamp( self.options.minVel, self.options.maxVel );
+
+                            body.isGrabbed = false;
+                            pos.body = body;
+
+                            delete body.isGrabbed;
+                        }
+
+                        // emit before we delete the vars in case
+                        // the listeners need the body
+                        self._world.emit('interact:release', pos);
+
+                        // remove vars
+                        delete self.bodyData[touchId];
+                    }
+                }
+            };
+        },
+
+        // extended
+        connect: function( world ){
+
+            // subscribe the .behave() method to the position integration step
+            world.on('integrate:positions', this.behave, this);
+
+            if ( window.PointerEvent ) {
+
+                this.el.addEventListener('pointerdown', this.grab);
+                window.addEventListener('pointermove', this.move);
+                window.addEventListener('pointerup', this.release);
+
+            } else {
+
+                this.el.addEventListener('mousedown', this.grab);
+                this.el.addEventListener('touchstart', this.grab);
+
+                window.addEventListener('mousemove', this.move);
+                window.addEventListener('touchmove', this.move);
+
+                window.addEventListener('mouseup', this.release);
+                window.addEventListener('touchend', this.release);
+
+            }
+        },
+
+        // extended
+        disconnect: function( world ){
+
+            // unsubscribe when disconnected
+            world.off('integrate:positions', this.behave, this);
+
+            if ( window.PointerEvent ) {
+
+                this.el.removeEventListener('pointerdown', this.grab);
+                window.removeEventListener('pointermove', this.move);
+                window.removeEventListener('pointerup', this.release);
+
+            } else {
+
+                this.el.removeEventListener('mousedown', this.grab);
+                this.el.removeEventListener('touchstart', this.grab);
+
+                window.removeEventListener('mousemove', this.move);
+                window.removeEventListener('touchmove', this.move);
+
+                window.removeEventListener('mouseup', this.release);
+                window.removeEventListener('touchend', this.release);
+
+            }
+        },
+
+        // extended
+        behave: function( data ){
+
+            var self = this
+                ,state
+                ,dt = Math.max(data.dt, self.options.moveThrottle)
+                ,body
+                ,d
+                ;
+
+            // if we have one or more bodies grabbed, we need to move them to the new mouse/finger positions.
+            // we'll do this by adjusting the velocity so they get there at the next step
+            for ( var touchId in self.bodyData ) {
+                d = self.bodyData[touchId];
+                body = d.body;
+                state = body.state;
+                state.vel.clone( d.pos ).vsub( d.offset ).vsub( state.pos ).mult( 1 / dt );
+            }
+        }
+    };
+});
+
+
+// ---
+// inside: src/behaviors/newtonian.js
+
+/**
+ * class NewtonianBehavior < Behavior
+ *
+ * `Physics.behavior('newtonian')`.
+ *
+ * Newtonian attraction between bodies (inverse square law).
+ *
+ * Additional options include:
+ * - strength: The strength of the interaction between bodies. (default: `1`)
+ * - max: The maximum distance between bodies at which to apply the behavior. (default: `false`... infinite)
+ * - min: The minimum distance between bodies at which to apply the behavior. (default: `false`... autocalculate)
+ **/
+Physics.behavior('newtonian', function( parent ){
+
+    var defaults = {
+
+        strength: 1,
+        // max distance to apply it to
+        max: false, // infinite
+        // min distance to apply it to
+        min: false // auto calc
+    };
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            var self = this;
+            // call parent init method
+            parent.init.call( this );
+            this.options.defaults( defaults );
+            this.options.onChange(function( opts ){
+                self._maxDistSq = opts.max === false ? Infinity : opts.max * opts.max;
+                self._minDistSq = opts.min ? opts.min * opts.min : 100 * opts.strength;
+            });
+            this.options( options );
+        },
+
+        calcPotential: function( posA, posB, out ){
+
+            var strength = this.options.strength
+                ,minDistSq = this._minDistSq
+                ,maxDistSq = this._maxDistSq
+                ,normsq
+                ,g
+                ,pos
+                ;
+
+            pos = out || new Physics.vector();
+
+            // clone the position
+            pos.clone( posB ).vsub( posA );
+            // get the square distance
+            normsq = pos.normSq();
+
+            if (normsq > minDistSq && normsq < maxDistSq){
+
+                g = strength / normsq;
+                return pos.normalize().mult( g );
+            }
+
+            return pos.zero();
+        },
+
+        // extended
+        behave: function( data ){
+
+            var bodies = this.getTargets()
+                ,body
+                ,other
+                ,scratch = Physics.scratchpad()
+                ,potential = scratch.vector()
+                ,comp
+                ,bodyA
+                ,bodyB
+                ,posA = scratch.vector()
+                ,posB = scratch.vector()
+                ,i, j, k, m, l, ll, lll
+                ;
+
+            for ( j = 0, l = bodies.length; j < l; j++ ){
+
+                body = bodies[ j ];
+
+                for ( i = j + 1; i < l; i++ ){
+
+                    other = bodies[ i ];
+
+                    if ( body.name === 'compound' ){
+                        comp = body;
+                    } else if ( other.name === 'compound' ){
+                        comp = other;
+                        other = body;
+                    }
+
+                    if ( comp ){
+                        if ( other.name === 'compound' ){
+                            for ( k = 0, ll = comp.children.length; k < ll; k++ ){
+                                bodyA = comp.children[ k ];
+                                comp.toWorldCoords( posA.clone( bodyA.state.pos ).vadd( comp.offset ) );
+                                for ( m = 0, lll = other.children.length; m < lll; m++ ){
+                                    bodyB = other.children[ m ];
+                                    other.toWorldCoords( posB.clone( bodyB.state.pos ).vadd( other.offset ) );
+                                    this.calcPotential( posA, posB, potential );
+                                    comp.accelerate( potential.mult( bodyB.mass ) );
+                                    other.accelerate( potential.mult( bodyA.mass/bodyB.mass ).negate() );
+                                }
+                            }
+                        } else {
+                            for ( k = 0, ll = comp.children.length; k < ll; k++ ){
+                                bodyA = comp.children[ k ];
+                                comp.toWorldCoords( posA.clone( bodyA.state.pos ).vadd( comp.offset ) );
+                                this.calcPotential( posA, other.state.pos, potential );
+                                comp.accelerate( potential.mult( other.mass ) );
+                                other.accelerate( potential.mult( bodyA.mass/other.mass ).negate() );
+                            }
+                        }
+
+                    } else {
+
+                        this.calcPotential( body.state.pos, other.state.pos, potential );
+                        body.accelerate( potential.mult( other.mass ) );
+                        other.accelerate( potential.mult( body.mass/other.mass ).negate() );
+                    }
+
+                    comp = null;
+                }
+            }
+
+            scratch.done();
+        }
+    };
+});
+
+
+// ---
+// inside: src/behaviors/sweep-prune.js
+
+/**
+ * class SweepPruneBehavior < Behavior
+ *
+ * `Physics.behavior('sweep-prune')`.
+ *
+ * Sweep and Prune implementation for broad phase collision detection.
+ *
+ * This massively improves the speed of collision detection. It's set up to always be used with [[BodyCollisionDetection]], and [[BodyImpulseResponse]].
+ *
+ * Additional options include:
+ * - channel: The channel to publish collision candidates to. (default: `collisions:candidates`)
+ **/
+Physics.behavior('sweep-prune', function( parent ){
+
+    var uid = 1;
+
+    // Get a unique numeric id for internal use
+    var getUniqueId = function getUniqueId(){
+
+        return uid++;
+    };
+
+    // add z: 2 to get this to work in 3D
+    var dof = { x: 0, y: 1 }; // degrees of freedom
+    // change to "3" to get it to work in 3D
+    var maxDof = 2;
+
+    var pairHash = Physics.util.pairHash;
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            parent.init.call( this );
+            this.options.defaults({
+                channel: 'collisions:candidates' //default channel
+            });
+            this.options( options );
+
+            this.encounters = [];
+            this.candidates = [];
+
+            this.clear();
+        },
+
+        /**
+         * SweepPruneBehavior#clear()
+         *
+         * Refresh tracking data
+         **/
+        clear: function(){
+
+            this.tracked = [];
+            this.pairs = []; // pairs selected as candidate collisions by broad phase
+            this.intervalLists = []; // stores lists of aabb projection intervals to be sorted
+
+            // init intervalLists
+            for ( var xyz = 0; xyz < maxDof; ++xyz ){
+
+                this.intervalLists[ xyz ] = [];
+            }
+        },
+
+        // extended
+        connect: function( world ){
+
+            world.on( 'add:body', this.trackBody, this );
+            world.on( 'remove:body', this.untrackBody, this );
+            world.on( 'integrate:positions', this.sweep, this, 1 );
+
+            // add current bodies
+            var bodies = world.getBodies();
+            for ( var i = 0, l = bodies.length; i < l; ++i ){
+
+                this.trackBody({ body: bodies[ i ] });
+            }
+        },
+
+        // extended
+        disconnect: function( world ){
+
+            world.off( 'add:body', this.trackBody, this );
+            world.off( 'remove:body', this.untrackBody, this );
+            world.off( 'integrate:positions', this.sweep, this, 1 );
+            this.clear();
+        },
+
+        /** internal
+         * SweepPruneBehavior#broadPhase() -> Array
+         * + (Array): The candidate data of overlapping aabbs
+         *
+         * Execute the broad phase and get candidate collisions
+         **/
+        broadPhase: function(){
+
+            this.updateIntervals();
+            this.sortIntervalLists();
+
+            if ( this._world ){
+                this._world.emit('sweep-prune:intervals', this.intervalLists);
+            }
+
+            return this.checkOverlaps();
+        },
+
+        /** internal
+         * SweepPruneBehavior#sortIntervalLists()
+         *
+         * Simple insertion sort for each axis
+         **/
+        sortIntervalLists: function(){
+
+            var list
+                ,len
+                ,i
+                ,hole
+                ,bound
+                ,boundVal
+                ,left
+                ,leftVal
+                ,axis
+                ;
+
+            // for each axis...
+            for ( var xyz = 0; xyz < maxDof; ++xyz ){
+
+                // get the intervals for that axis
+                list = this.intervalLists[ xyz ];
+                i = 0;
+                len = list.length;
+                axis = xyz;
+
+                // for each interval bound...
+                while ( (++i) < len ){
+
+                    // store bound
+                    bound = list[ i ];
+                    boundVal = bound.val.get( axis );
+                    hole = i;
+
+                    left = list[ hole - 1 ];
+                    leftVal = left && left.val.get( axis );
+
+                    // while others are greater than bound...
+                    while (
+                        hole > 0 &&
+                        (
+                            leftVal > boundVal ||
+                            // if it's an equality, only move it over if
+                            // the hole was created by a minimum
+                            // and the previous is a maximum
+                            // so that we detect contacts also
+                            leftVal === boundVal &&
+                            ( left.type && !bound.type )
+                        )
+                    ) {
+
+                        // move others greater than bound to the right
+                        list[ hole ] = left;
+                        hole--;
+                        left = list[ hole - 1 ];
+                        leftVal = left && left.val.get( axis );
+                    }
+
+                    // insert bound in the hole
+                    list[ hole ] = bound;
+                }
+            }
+        },
+
+        /** internal
+         * SweepPruneBehavior#getPair( tr1, tr2, doCreate ) -> Object
+         * - tr1 (Object): First tracker
+         * - tr2 (Object): Second tracker
+         * - doCreate (Boolean): Create if not found
+         * + (Object): Pair object or null if not found
+         *
+         * Get a pair object for the tracker objects
+         **/
+        getPair: function(tr1, tr2, doCreate){
+
+            var hash = pairHash( tr1.id, tr2.id );
+
+            if ( hash === false ){
+                return null;
+            }
+
+            var c = this.pairs[ hash ];
+
+            if ( !c ){
+
+                if ( !doCreate ){
+                    return null;
+                }
+
+                c = this.pairs[ hash ] = {
+                    bodyA: tr1.body,
+                    bodyB: tr2.body,
+                    flag: 1
+                };
+            }
+
+            if ( doCreate){
+                c.flag = 1;
+            }
+
+            return c;
+        },
+
+        // getPair: function(tr1, tr2, doCreate){
+
+        //     var hash = Math.min(tr1.id, tr2.id) // = pairHash( tr1.id, tr2.id )
+        //         ,other = Math.max(tr1.id, tr2.id)
+        //         ,first
+        //         ,c
+        //         ;
+
+        //     if ( hash === false ){
+        //         return null;
+        //     }
+
+        //     first = this.pairs[ hash ];
+
+        //     if ( !first ){
+        //         if ( !doCreate ){
+        //             return null;
+        //         }
+
+        //         first = this.pairs[ hash ] = [];
+        //     }
+
+        //     c = first[ other ];
+
+        //     if ( !c ){
+
+        //         if ( !doCreate ){
+        //             return null;
+        //         }
+
+        //         c = first[ other ] = {
+        //             bodyA: tr1.body,
+        //             bodyB: tr2.body,
+        //             flag: 1
+        //         };
+        //     }
+
+        //     return c;
+        // },
+
+        /** internal
+         * SweepPruneBehavior#checkOverlaps() -> Array
+         * + (Array): List of candidate collisions
+         *
+         * Check each axis for overlaps of bodies AABBs
+         **/
+        checkOverlaps: function(){
+
+            var isX
+                ,hash
+                ,tr1
+                ,tr2
+                ,bound
+                ,list
+                ,len
+                ,i
+                ,j
+                ,c
+                // determine which axis is the last we need to check
+                ,collisionFlag = 1 << (dof.z + 1) << (dof.y + 1) << (dof.x + 1)
+                ,encounters = this.encounters
+                ,enclen = 0
+                ,candidates = this.candidates
+                ;
+
+            Physics.util.clearArray( encounters );
+            Physics.util.clearArray( candidates );
+
+            for ( var xyz = 0; xyz < maxDof; ++xyz ){
+
+                // is the x coord
+                isX = (xyz === 0);
+                // get the interval list for this axis
+                list = this.intervalLists[ xyz ];
+
+                // for each interval bound
+                for ( i = 0, len = list.length; i < len; i++ ){
+
+                    bound = list[ i ];
+                    tr1 = bound.tracker;
+
+                    if ( bound.type ){
+
+                        // is a max
+
+                        j = enclen;
+
+                        for ( j = enclen - 1; j >= 0; j-- ){
+
+                            tr2 = encounters[ j ];
+
+                            // if they are the same tracked interval
+                            if ( tr2 === tr1 ){
+
+                                // remove the interval from the encounters list
+                                // faster than .splice()
+                                if ( j < enclen - 1 ) {
+
+                                    encounters[ j ] = encounters.pop();
+
+                                } else {
+
+                                    // encountered a max right after a min... no overlap
+                                    encounters.pop();
+                                }
+
+                                enclen--;
+
+                            } else {
+
+                                // check if we have flagged this pair before
+                                // if it's the x axis, create a pair
+                                c = this.getPair( tr1, tr2, isX );
+
+                                if ( c && c.flag < collisionFlag ){
+
+                                    // if it's greater than the axis index, set the flag
+                                    // to = 0.
+                                    // if not, increment the flag by one.
+                                    c.flag = c.flag << (xyz + 1);
+
+                                    // c.flag will equal collisionFlag
+                                    // if we've incremented the flag
+                                    // enough that all axes are overlapping
+                                    if ( c.flag === collisionFlag ){
+
+                                        // overlaps on all axes.
+                                        // add it to possible collision
+                                        // candidates list for narrow phase
+
+                                        candidates.push( c );
+                                    }
+                                }
+                            }
+                        }
+
+                    } else {
+
+                        // is a min
+                        // just add this minimum to the encounters list
+                        enclen = encounters.push( tr1 );
+                    }
+                }
+            }
+
+            return candidates;
+        },
+
+        /** internal
+         * SweepPruneBehavior#updateIntervals()
+         *
+         * Update position intervals on each axis
+         **/
+        updateIntervals: function(){
+
+            var tr
+                ,intr
+                ,aabb
+                ,list = this.tracked
+                ,i = list.length
+                ;
+
+            // for all tracked bodies
+            while ( (--i) >= 0 ){
+
+                tr = list[ i ];
+                intr = tr.interval;
+                aabb = tr.body.aabb();
+
+                // copy the position (plus or minus) the aabb half-dimensions
+                // into the min/max intervals
+                intr.min.val.clone( aabb ).sub( aabb.hw, aabb.hh );
+                intr.max.val.clone( aabb ).add( aabb.hw, aabb.hh );
+            }
+        },
+
+        /** internal
+         * SweepPruneBehavior#trackBody( data )
+         * - data (Object): Event data
+         *
+         * Event callback to add body to list of those tracked by sweep and prune
+         **/
+        trackBody: function( data ){
+
+            var body = data.body
+                ,tracker = {
+
+                    id: getUniqueId(),
+                    body: body
+                }
+                ,intr = {
+
+                    min: {
+                        type: false, //min
+                        val: new Physics.vector(),
+                        tracker: tracker
+                    },
+
+                    max: {
+                        type: true, //max
+                        val: new Physics.vector(),
+                        tracker: tracker
+                    }
+                }
+                ;
+
+            tracker.interval = intr;
+            this.tracked.push( tracker );
+
+            for ( var xyz = 0; xyz < maxDof; ++xyz ){
+
+                this.intervalLists[ xyz ].push( intr.min, intr.max );
+            }
+        },
+
+        /** internal
+         * SweepPruneBehavior#untrackBody( data )
+         * - data (Object): Event data
+         *
+         * Event callback to remove body from list of those tracked
+         **/
+        untrackBody: function( data ){
+
+            var body = data.body
+                ,list
+                ,minmax
+                ,trackedList = this.tracked
+                ,tracker
+                ,count
+                ;
+
+            for ( var i = 0, l = trackedList.length; i < l; ++i ){
+
+                tracker = trackedList[ i ];
+
+                if ( tracker.body === body ){
+
+                    // remove the tracker at this index
+                    trackedList.splice(i, 1);
+
+                    for ( var xyz = 0; xyz < maxDof; ++xyz ){
+
+                        count = 0;
+                        list = this.intervalLists[ xyz ];
+
+                        for ( var j = 0, m = list.length; j < m; ++j ){
+
+                            minmax = list[ j ];
+
+                            if ( minmax === tracker.interval.min || minmax === tracker.interval.max ){
+
+                                // remove interval from list
+                                list.splice(j, 1);
+                                j--;
+                                l--;
+
+                                if (count > 0){
+                                    break;
+                                }
+
+                                count++;
+                            }
+                        }
+                    }
+
+                    break;
+                }
+            }
+        },
+
+        /** internal
+         * SweepPruneBehavior#sweep( data )
+         * - data (Object): Event data
+         *
+         * Event callback to sweep and publish event if any candidate collisions are found
+         **/
+        sweep: function( data ){
+
+            var self = this
+                ,candidates
+                ;
+
+            candidates = self.broadPhase();
+
+            if ( candidates.length ){
+
+                this._world.emit( this.options.channel, {
+                    candidates: candidates
+                });
+            }
+        }
+    };
+});
+
+
+// ---
+// inside: src/behaviors/verlet-constraints.js
+
+/**
+ * class VerletConstraintsBehavior < Behavior
+ *
+ * `Physics.behavior('verlet-constraints')`.
+ *
+ * Verlet constraints manager.
+ *
+ * Handles distance constraints, and angle constraints
+ *
+ * Additional options include:
+ * - iterations: The number of iterations to take to relax the constraints. (default: `2`)
+ **/
+Physics.behavior('verlet-constraints', function( parent ){
+
+    var TWOPI = 2 * Math.PI;
+
+    var defaults = {
+
+        // number of iterations to resolve constraints
+        iterations: 2
+    };
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            parent.init.call( this );
+            this.options.defaults( defaults );
+            this.options( options );
+
+            this._distanceConstraints = [];
+            this._angleConstraints = [];
+        },
+
+        // extended
+        connect: function( world ){
+
+            var intg = world.integrator();
+
+            if ( intg && intg.name.indexOf('verlet') < 0 ){
+
+                throw 'The rigid constraint manager needs a world with a "verlet" compatible integrator.';
+            }
+
+            world.on('integrate:positions', this.resolve, this);
+        },
+
+        // extended
+        disconnect: function( world ){
+
+            world.off('integrate:positions', this.resolve, this);
+        },
+
+        /**
+         * VerletConstraintsBehavior#drop() -> this
+         *
+         * Remove all constraints
+         **/
+        drop: function(){
+
+            // drop the current constraints
+            this._distanceConstraints = [];
+            this._angleConstraints = [];
+            return this;
+        },
+
+        /**
+         * VerletConstraintsBehavior#distanceConstraint( bodyA, bodyB[, stiffness, targetLength] ) -> Object
+         * - bodyA (Body): First body
+         * - bodyB (Body): Second body
+         * - stiffness (Number): A number between 0 and 1 that represents the stiffness of the constraint. Defaults to: `0.5`
+         * - targetLength (Number): Target length. defaults to current distance between the bodies
+         * + (Object): The constraint data object
+         *
+         * Constrain two bodies to a target relative distance.
+         *
+         * Returns constraint data that can be used to remove the constraint later.
+         *
+         * - `.bodyA` and `.bodyB` are references to the bodies
+         * - `.type` is the type of constraint
+         * - `.id` is the string ID of the constraint
+         * - `.stiffness` is the stiffness
+         * - `.targetLength` is the target length
+         **/
+        distanceConstraint: function( bodyA, bodyB, stiffness, targetLength ){
+
+            var cst;
+
+            if (!bodyA || !bodyB){
+
+                return false;
+            }
+
+            cst = {
+                id: Physics.util.uniqueId('dis-constraint'),
+                type: 'dis',
+                bodyA: bodyA,
+                bodyB: bodyB,
+                stiffness: stiffness || 0.5,
+                targetLength: targetLength || bodyB.state.pos.dist( bodyA.state.pos )
+            };
+
+            cst.targetLengthSq = cst.targetLength * cst.targetLength;
+
+            this._distanceConstraints.push( cst );
+            return cst;
+        },
+
+        /**
+         * VerletConstraintsBehavior#angleConstraint( bodyA, bodyB, bodyC[, stiffness, targetAngle] ) -> Object
+         * - bodyA (Body): First body
+         * - bodyB (Body): Second body
+         * - bodyC (Body): Third body
+         * - stiffness (Number): A number between 0 and 1 that represents the stiffness of the constraint. Defaults to: `0.5`
+         * - targetAngle (Number): Target angle. Defaults to the current angle between bodies
+         * + (Object): The constraint data object
+         *
+         * Constrain three bodies to a target relative angle
+         *
+         * Returns constraint data that can be used to remove the constraint later.
+         *
+         * - `.bodyA`, `.bodyB`, and `.bodyC` are references to the bodies
+         * - `.type` is the type of constraint
+         * - `.id` is the string ID of the constraint
+         * - `.stiffness` is the stiffness
+         * - `.targetAngle` is the target angle
+         **/
+        angleConstraint: function( bodyA, bodyB, bodyC, stiffness, targetAngle ){
+
+            var cst;
+
+            if (!bodyA || !bodyB){
+
+                return false;
+            }
+
+            cst = {
+                id: Physics.util.uniqueId('ang-constraint'),
+                type: 'ang',
+                bodyA: bodyA,
+                bodyB: bodyB,
+                bodyC: bodyC,
+                stiffness: stiffness || 0.5,
+                targetAngle: targetAngle || bodyB.state.pos.angle2( bodyA.state.pos, bodyC.state.pos )
+            };
+
+            this._angleConstraints.push( cst );
+            return cst;
+        },
+
+        /**
+         * VerletConstraintsBehavior#remove( constraintData ) -> this
+         * VerletConstraintsBehavior#remove( constraintId ) -> this
+         * - constraintData (Object): The constraint data returned when creating a constraint
+         * - constraintId (String): The constraint id
+         *
+         * Remove a constraint
+         **/
+        remove: function( cstrOrId ){
+
+            var constraints
+                ,type
+                ,isObj
+                ,i
+                ,l
+                ;
+
+            isObj = Physics.util.isObject( cstrOrId );
+
+            type = (isObj) ? cstrOrId.type : cstrOrId.substr(0, 3);
+            constraints = ( type === 'ang' ) ? this._angleConstraints : this._distanceConstraints;
+
+            if ( isObj ){
+
+                for ( i = 0, l = constraints.length; i < l; ++i ){
+
+                    if ( constraints[ i ] === cstrOrId ){
+
+                        constraints.splice( i, 1 );
+                        return this;
+                    }
+                }
+            } else {
+
+                for ( i = 0, l = constraints.length; i < l; ++i ){
+
+                    if ( constraints[ i ].id === cstrOrId ){
+
+                        constraints.splice( i, 1 );
+                        return this;
+                    }
+                }
+            }
+
+            return this;
+        },
+
+        /** internal
+         * VerletConstraintsBehavior#resolveAngleConstraints( coef )
+         * - coef (Number): Coefficient for this resolution phase
+         *
+         * Resolve angle constraints.
+         **/
+        resolveAngleConstraints: function( coef ){
+
+            var constraints = this._angleConstraints
+                ,scratch = Physics.scratchpad()
+                ,trans = scratch.transform()
+                ,con
+                ,ang
+                ,corr
+                ,proportion
+                ,invMassSum
+                ;
+
+            for ( var i = 0, l = constraints.length; i < l; ++i ){
+
+                con = constraints[ i ];
+
+                ang = con.bodyB.state.pos.angle2( con.bodyA.state.pos, con.bodyC.state.pos );
+                corr = ang - con.targetAngle;
+
+                if (!corr){
+
+                    continue;
+
+                } else if (corr <= -Math.PI){
+
+                    corr += TWOPI;
+
+                } else if (corr >= Math.PI){
+
+                    corr -= TWOPI;
+                }
+
+                trans.setTranslation( con.bodyB.state.pos );
+
+                corr *= -coef * con.stiffness;
+
+                if ( con.bodyA.treatment === 'dynamic' && con.bodyB.treatment === 'dynamic' && con.bodyC.treatment === 'dynamic' ){
+                    invMassSum = 1 / (con.bodyA.mass + con.bodyB.mass + con.bodyC.mass);
+                }
+
+                if ( con.bodyA.treatment === 'dynamic' ){
+
+                    if ( con.bodyB.treatment === 'dynamic' && con.bodyC.treatment === 'dynamic' ){
+
+                        ang = corr * (con.bodyB.mass + con.bodyC.mass) * invMassSum;
+
+                    } else if ( con.bodyB.treatment !== 'dynamic' ){
+
+                        ang = corr * con.bodyC.mass / ( con.bodyC.mass + con.bodyA.mass );
+
+                    } else {
+
+                        ang = corr * con.bodyB.mass / ( con.bodyB.mass + con.bodyA.mass );
+                    }
+
+
+                    trans.setRotation( ang );
+                    con.bodyA.state.pos.translateInv( trans );
+                    con.bodyA.state.pos.rotate( trans );
+                    con.bodyA.state.pos.translate( trans );
+                }
+
+                if ( con.bodyC.treatment === 'dynamic' ){
+
+                    if ( con.bodyA.treatment === 'dynamic' && con.bodyB.treatment === 'dynamic' ){
+
+                        ang = -corr * (con.bodyB.mass + con.bodyA.mass) * invMassSum;
+
+                    } else if ( con.bodyB.treatment !== 'dynamic' ){
+
+                        ang = -corr * con.bodyA.mass / ( con.bodyC.mass + con.bodyA.mass );
+
+                    } else {
+
+                        ang = -corr * con.bodyB.mass / ( con.bodyB.mass + con.bodyC.mass );
+                    }
+
+                    trans.setRotation( ang );
+                    con.bodyC.state.pos.translateInv( trans );
+                    con.bodyC.state.pos.rotate( trans );
+                    con.bodyC.state.pos.translate( trans );
+                }
+
+                if ( con.bodyB.treatment === 'dynamic' ){
+
+                    if ( con.bodyA.treatment === 'dynamic' && con.bodyC.treatment === 'dynamic' ){
+
+                        ang = corr * (con.bodyA.mass + con.bodyC.mass) * invMassSum;
+
+                    } else if ( con.bodyA.treatment !== 'dynamic' ){
+
+                        ang = corr * con.bodyC.mass / ( con.bodyC.mass + con.bodyB.mass );
+
+                    } else {
+
+                        ang = corr * con.bodyA.mass / ( con.bodyA.mass + con.bodyC.mass );
+                    }
+
+                    // ang = corr;
+
+                    trans.setRotation( ang ).setTranslation( con.bodyA.state.pos );
+                    con.bodyB.state.pos.translateInv( trans );
+                    con.bodyB.state.pos.rotate( trans );
+                    con.bodyB.state.pos.translate( trans );
+
+                    trans.setTranslation( con.bodyC.state.pos );
+                    con.bodyB.state.pos.translateInv( trans );
+                    con.bodyB.state.pos.rotateInv( trans );
+                    con.bodyB.state.pos.translate( trans );
+                }
+
+                con.bodyA.sleepCheck();
+                con.bodyB.sleepCheck();
+                con.bodyC.sleepCheck();
+            }
+
+            scratch.done();
+        },
+
+        /** internal
+         * VerletConstraintsBehavior#resolveDistanceConstraints( coef )
+         * - coef (Number): Coefficient for this resolution phase
+         *
+         * Resolve distance constraints.
+         **/
+        resolveDistanceConstraints: function( coef ){
+
+            var constraints = this._distanceConstraints
+                ,scratch = Physics.scratchpad()
+                ,BA = scratch.vector()
+                ,con
+                ,len
+                ,corr
+                ,proportion
+                ;
+
+            for ( var i = 0, l = constraints.length; i < l; ++i ){
+
+                con = constraints[ i ];
+
+                // move constrained bodies to target length based on their
+                // mass proportions
+                BA.clone( con.bodyB.state.pos ).vsub( con.bodyA.state.pos );
+                len = BA.normSq() || Math.random() * 0.0001;
+                corr = coef * con.stiffness * ( len - con.targetLengthSq ) / len;
+
+                BA.mult( corr );
+                proportion = (con.bodyA.treatment !== 'dynamic' || con.bodyB.treatment !== 'dynamic') ? 1 : con.bodyB.mass / (con.bodyA.mass + con.bodyB.mass);
+
+                if ( con.bodyA.treatment === 'dynamic' ){
+
+                    if ( con.bodyB.treatment === 'dynamic' ){
+                        BA.mult( proportion );
+                    }
+
+                    con.bodyA.state.pos.vadd( BA );
+
+                    if ( con.bodyB.treatment === 'dynamic' ){
+                        BA.mult( 1 / proportion );
+                    }
+                }
+
+                if ( con.bodyB.treatment === 'dynamic' ){
+
+                    if ( con.bodyA.treatment === 'dynamic' ){
+                        BA.mult( 1 - proportion );
+                    }
+
+                    con.bodyB.state.pos.vsub( BA );
+                }
+
+                con.bodyA.sleepCheck();
+                con.bodyB.sleepCheck();
+            }
+
+            scratch.done();
+        },
+
+        /** internal
+         * VerletConstraintsBehavior#shuffleConstraints()
+         *
+         * Mix up the constraints.
+         **/
+        shuffleConstraints: function(){
+
+            this._distanceConstraints = Physics.util.shuffle( this._distanceConstraints );
+            this._angleConstraints = Physics.util.shuffle( this._angleConstraints );
+        },
+
+        /** internal
+         * VerletConstraintsBehavior#resolve()
+         *
+         * Resolve all constraints.
+         **/
+        resolve: function(){
+
+            var its = this.options.iterations
+                ,coef = 1 / its
+                ;
+
+            for (var i = 0; i < its; i++){
+
+                // this.shuffleConstraints();
+                this.resolveDistanceConstraints( coef );
+                this.resolveAngleConstraints( coef );
+            }
+        },
+
+        /**
+         * VerletConstraintsBehavior#getConstraints() -> Object
+         * + (Object): The object containing copied arrays of the constraints
+         *
+         * Get all constraints.
+         **/
+        getConstraints: function(){
+
+            return {
+                distanceConstraints: [].concat(this._distanceConstraints),
+                angleConstraints: [].concat(this._angleConstraints)
+            };
+        }
+    };
+});
+
+
+// ---
+// inside: src/integrators/improved-euler.js
+
+Physics.integrator('improved-euler', function( parent ){
+
+    return {
+        /**
+         * class ImprovedEuler < Integrator
+         *
+         * `Physics.integrator('improved-euler')`.
+         *
+         * The improved euler integrator.
+         **/
+
+        // extended
+        init: function( options ){
+
+            // call parent init
+            parent.init.call(this, options);
+        },
+
+        // extended
+        integrateVelocities: function( bodies, dt ){
+
+            // half the timestep squared
+            var drag = 1 - this.options.drag
+                ,body = null
+                ,state
+                ;
+
+            for ( var i = 0, l = bodies.length; i < l; ++i ){
+
+                body = bodies[ i ];
+                state = body.state;
+
+                // only integrate if the body isn't fixed
+                if ( body.treatment !== 'static' && !body.sleep( dt ) ){
+
+                    // Inspired from https://github.com/soulwire/Coffee-Physics
+                    // @licence MIT
+                    //
+                    // x += (v * dt) + (a * 0.5 * dt * dt)
+                    // v += a * dt
+
+
+                    // Scale force to mass.
+                    // state.acc.mult( body.massInv );
+
+                    // Remember velocity for future use.
+                    state.old.vel.clone( state.vel );
+
+                    // remember original acc
+                    state.old.acc.clone( state.acc );
+
+                    // Update velocity first so we can reuse the acc vector.
+                    // a *= dt
+                    // v += a ...
+                    state.vel.vadd( state.acc.mult( dt ) );
+
+                    // Apply "air resistance".
+                    if ( drag ){
+
+                        state.vel.mult( drag );
+                    }
+
+                    // Reset accel
+                    state.acc.zero();
+
+                    //
+                    // Angular components
+                    //
+
+                    state.old.angular.vel = state.angular.vel;
+                    state.angular.vel += state.angular.acc * dt;
+                    state.angular.acc = 0;
+
+                } else {
+                    // set the velocity and acceleration to zero!
+                    state.vel.zero();
+                    state.acc.zero();
+                    state.angular.vel = 0;
+                    state.angular.acc = 0;
+                }
+            }
+        },
+
+        // extended
+        integratePositions: function( bodies, dt ){
+
+            // half the timestep squared
+            var halfdtdt = 0.5 * dt * dt
+                ,body = null
+                ,state
+                // use cached vector instances
+                // so we don't need to recreate them in a loop
+                ,scratch = Physics.scratchpad()
+                ,vel = scratch.vector()
+                ,angVel
+                ;
+
+            for ( var i = 0, l = bodies.length; i < l; ++i ){
+
+                body = bodies[ i ];
+                state = body.state;
+
+                // only integrate if the body isn't fixed
+                if ( body.treatment !== 'static' && !body.sleep() ){
+
+
+                    // Store previous location.
+                    state.old.pos.clone( state.pos );
+
+                    // Update position.
+                    // ...
+                    // oldV *= dt
+                    // a *= 0.5 * dt
+                    // x += oldV + a
+                    vel.clone( state.old.vel );
+                    state.pos.vadd( vel.mult( dt ) ).vadd( state.old.acc.mult( halfdtdt ) );
+
+                    state.old.acc.zero();
+
+                    //
+                    // Angular components
+                    //
+
+                    state.old.angular.pos = state.angular.pos;
+                    state.angular.pos += state.old.angular.vel * dt + state.old.angular.acc * halfdtdt;
+                    state.old.angular.acc = 0;
+
+                }
+            }
+
+            scratch.done();
+        }
+    };
+});
+
+
+// ---
+// inside: src/integrators/velocity-verlet-alt.js
+
+Physics.integrator('velocity-verlet-alt', function( parent ){
+
+    // for this integrator we need to know if the object has been integrated before
+    // so let's add a mixin to bodies
+
+    Physics.body.mixin({
+
+        started: function( val ){
+            if ( val !== undefined ){
+                this._started = true;
+            }
+
+            return !!this._started;
+        }
+    });
+
+
+    return {
+        /**
+         * class VelocityVerlet < Integrator
+         *
+         * `Physics.integrator('velocity-verlet')`.
+         *
+         * The velocity-verlet integrator.
+         **/
+
+        // extended
+        init: function( options ){
+
+            // call parent init
+            parent.init.call(this, options);
+        },
+
+        // extended
+        integrateVelocities: function( bodies, dt ){
+
+            // half the timestep
+            var dtdt = dt * dt
+                ,drag = 1 - this.options.drag
+                ,body = null
+                ,state
+                ;
+
+            for ( var i = 0, l = bodies.length; i < l; ++i ){
+
+                body = bodies[ i ];
+                state = body.state;
+
+                // only integrate if the body isn't static
+                if ( body.treatment !== 'static' ){
+
+                    // v = v_prev + 0.5 * (a_prev + a) * dt
+                    // x = x_prev + v_prev * dt + 0.5 * a_prev * dt * dt
+
+                    // use the velocity in vel if the velocity has been changed manually
+                    if ( !body.started() ){
+
+                        // Set old vals on first integration
+                        state.old.acc.clone( state.acc );
+                        state.old.acc.mult( dt );
+                        state.old.vel.clone( state.vel ).vsub( state.old.acc );
+                        state.old.acc.mult( 1/dt );
+                    }
+
+                    // Apply "air resistance".
+                    if ( drag ){
+
+                        state.vel.mult( drag );
+                    }
+
+                    // Apply acceleration
+                    // v += 0.5 * (a_prev + a) * dt
+                    state.vel.vadd( state.old.acc.vadd( state.acc ).mult( 0.5 * dt ) );
+
+                    // Reset accel
+                    // state.acc.zero();
+
+                    //
+                    // Angular components
+                    //
+
+                    if ( !body.started() ){
+
+                        // Set old vals on first integration
+                        state.old.angular.acc = state.angular.acc;
+                        state.old.angular.vel = state.angular.vel - state.old.angular.acc * dt;
+                    }
+
+                    state.angular.vel += 0.5 * (state.angular.acc + state.old.angular.acc) * dt;
+                    state.angular.acc = 0;
+
+                    body.started( true );
+
+                } else {
+                    // set the velocity and acceleration to zero!
+                    state.vel.zero();
+                    state.acc.zero();
+                    state.angular.vel = 0;
+                    state.angular.acc = 0;
+                }
+            }
+        },
+
+        // extended
+        integratePositions: function( bodies, dt ){
+
+            // half the timestep
+            var dtdt = dt * dt
+                ,body = null
+                ,state
+                ;
+
+            for ( var i = 0, l = bodies.length; i < l; ++i ){
+
+                body = bodies[ i ];
+                state = body.state;
+
+                // only integrate if the body isn't static
+                if ( body.treatment !== 'static' ){
+
+                    // x = x_prev + v_prev * dt + 0.5 * a_prev * dt * dt
+
+                    // Store old position.
+                    // xold = x
+                    state.old.pos.clone( state.pos );
+
+                    state.old.vel.mult( dt );
+                    state.old.acc.mult( 0.5 * dtdt );
+                    state.pos.vadd( state.old.vel ).vadd( state.old.acc );
+
+                    // store calculated velocity
+                    state.old.vel.clone( state.vel );
+
+                    // store old acc
+                    state.old.acc.clone( state.acc );
+
+                    // Reset accel
+                    state.acc.zero();
+
+                    //
+                    // Angular components
+                    //
+                    state.old.angular.pos = state.angular.pos;
+
+                    state.angular.pos += state.angular.vel * dt + 0.5 * state.old.angular.acc * dtdt;
+                    state.old.angular.vel = state.angular.vel;
+                    state.old.angular.acc = state.angular.acc;
+                    state.angular.acc = 0;
+                }
+            }
+        }
+    };
+});
+
+
+// ---
+// inside: src/integrators/velocity-verlet.js
+
+Physics.integrator('velocity-verlet', function( parent ){
+
+    // for this integrator we need to know if the object has been integrated before
+    // so let's add a mixin to bodies
+
+    Physics.body.mixin({
+
+        started: function( val ){
+            if ( val !== undefined ){
+                this._started = true;
+            }
+
+            return !!this._started;
+        }
+    });
+
+
+    return {
+        /**
+         * class VelocityVerlet < Integrator
+         *
+         * `Physics.integrator('velocity-verlet')`.
+         *
+         * The velocity-verlet integrator.
+         **/
+
+        // extended
+        init: function( options ){
+
+            // call parent init
+            parent.init.call(this, options);
+        },
+
+        /**
+         * Integrator#integrate( bodies, dt ) -> this
+         * - bodies (Array): List of bodies to integrate
+         * - dt (Number): Timestep size
+         *
+         * Integrate bodies by timestep.
+         *
+         * Will emit `integrate:velocities` and `integrate:positions`
+         * events on the world.
+         **/
+        integrate: function( bodies, dt ){
+
+            var world = this._world;
+
+            this.integratePositions( bodies, dt );
+
+            if ( world ){
+                world.emit('integrate:positions', {
+                    bodies: bodies,
+                    dt: dt
+                });
+            }
+
+            this.integrateVelocities( bodies, dt );
+
+            if ( world ){
+                world.emit('integrate:velocities', {
+                    bodies: bodies,
+                    dt: dt
+                });
+            }
+
+            return this;
+        },
+
+        // extended
+        integrateVelocities: function( bodies, dt ){
+
+            // half the timestep
+            var dtdt = dt * dt
+                ,drag = 1 - this.options.drag
+                ,body = null
+                ,state
+                ;
+
+            for ( var i = 0, l = bodies.length; i < l; ++i ){
+
+                body = bodies[ i ];
+                state = body.state;
+
+                // only integrate if the body isn't static
+                if ( body.treatment !== 'static' && !body.sleep() ){
+
+                    // v = v_prev + 0.5 * (a_prev + a) * dt
+                    // x = x_prev + v_prev * dt + 0.5 * a_prev * dt * dt
+
+                    // Apply "air resistance".
+                    if ( drag ){
+
+                        state.vel.mult( drag );
+                    }
+
+                    // Apply acceleration
+                    // v += 0.5 * (a_prev + a) * dt
+                    state.old.vel.clone( state.vel );
+                    state.vel.vadd( state.old.acc.vadd( state.acc ).mult( 0.5 * dt ) );
+
+                    // Reset accel
+                    state.old.acc.clone( state.acc );
+                    state.acc.zero();
+
+                    //
+                    // Angular components
+                    //
+
+                    state.old.angular.vel = state.angular.vel;
+                    state.old.angular.acc = state.angular.acc;
+
+                    state.angular.vel += 0.5 * (state.angular.acc + state.old.angular.acc) * dt;
+
+                    state.angular.acc = 0;
+
+                    body.started( true );
+
+                } else {
+                    // set the velocity and acceleration to zero!
+                    state.vel.zero();
+                    state.acc.zero();
+                    state.angular.vel = 0;
+                    state.angular.acc = 0;
+                }
+            }
+        },
+
+        // extended
+        integratePositions: function( bodies, dt ){
+
+            // half the timestep
+            var dtdt = dt * dt
+                ,body = null
+                ,state
+                ;
+
+            for ( var i = 0, l = bodies.length; i < l; ++i ){
+
+                body = bodies[ i ];
+                state = body.state;
+
+                // only integrate if the body isn't static
+                if ( body.treatment !== 'static' && !body.sleep( dt ) ){
+
+                    // x = x_prev + v_prev * dt + 0.5 * a_prev * dt * dt
+
+                    // use the velocity in vel if the velocity has been changed manually
+                    if ( !body.started() ){
+
+                        // Set old vals on first integration
+                        state.old.acc.clone( state.acc );
+                        state.old.acc.mult( dt );
+                        state.old.vel.clone( state.vel ).vsub( state.old.acc );
+                        state.old.acc.mult( 1/dt );
+                    }
+
+                    // Store old position.
+                    // xold = x
+                    state.old.pos.clone( state.pos );
+
+                    state.old.vel.mult( dt );
+                    state.old.acc.mult( 0.5 * dtdt );
+                    state.pos.vadd( state.old.vel ).vadd( state.old.acc );
+
+                    // revert
+                    state.old.vel.mult( 1/dt );
+                    state.old.acc.mult( 2 / dtdt );
+
+                    //
+                    // Angular components
+                    //
+
+                    if ( !body.started() ){
+
+                        // Set old vals on first integration
+                        state.old.angular.acc = state.angular.acc;
+                        state.old.angular.vel = state.angular.vel - state.old.angular.acc * dt;
+                    }
+
+                    state.old.angular.pos = state.angular.pos;
+
+                    state.angular.pos += state.angular.vel * dt + 0.5 * state.old.angular.acc * dtdt;
+                }
+            }
+        }
+    };
+});
+
+
+// ---
+// inside: src/renderers/canvas.js
+
+/**
+ * class CanvasRenderer < Renderer
+ *
+ * Physics.renderer('canvas')
+ *
+ * Renderer that uses HTMLCanvas to render the world bodies.
+ *
+ * Additional config options:
+ *
+ * - metaEl: HTMLElement to write meta information like FPS and IPF into. (default: autogenerated)
+ * - offset: Offset the shapes by this amount. (default: `{ x: 0, y: 0 }`)
+ * - styles: Styles to use to draw the shapes. (see below)
+ *
+ * The styles property should contain _default_ styles for each shape you want to draw.
+ *
+ * Example:
+ *
+ * ```javascript
+ * styles: {
+ *
+ *    'circle' : {
+ *        strokeStyle: '#542437',
+ *        lineWidth: 1,
+ *        fillStyle: '#542437',
+ *        angleIndicator: 'white'
+ *    },
+ *
+ *    'convex-polygon' : {
+ *        strokeStyle: '#542437',
+ *        lineWidth: 1,
+ *        fillStyle: '#542437',
+ *        angleIndicator: 'white'
+ *    }
+ * }
+ * ```
+ *
+ * Styles can also be defined on a per-body basis. Use the "styles" property for a body:
+ *
+ * Example:
+ *
+ * ```javascript
+ * Physics.body('circle', {
+ *     // ...
+ *     styles: {
+ *        strokeStyle: '#542437',
+ *        lineWidth: 1,
+ *        fillStyle: '#542437',
+ *        angleIndicator: 'white'
+ *    }
+ * });
+ * ```
+ *
+ * You can also define an image to use for a body:
+ *
+ * Example:
+ *
+ * ```javascript
+ * Physics.body('circle', {
+ *     // ...
+ *     styles: {
+ *        src: 'path/to/image.jpg',
+ *        width: 40,
+ *        height: 50
+ *    }
+ * });
+ * ```
+ **/
+Physics.renderer('canvas', function( proto ){
+
+    if ( !document ){
+        // must be in node environment
+        return {};
+    }
+
+    var Pi2 = Math.PI * 2
+        // helper to create new dom elements
+        ,newEl = function( node, content ){
+            var el = document.createElement(node || 'div');
+            if (content){
+                el.innerHTML = content;
+            }
+            return el;
+        }
+        ,colors = {
+            white: '#fff'
+            ,violet: '#542437'
+            ,blue: '#53777A'
+        }
+        ;
+
+    var defaults = {
+
+        // the element to place meta data into
+        metaEl: null,
+        // default styles of drawn objects
+        styles: {
+
+            'point': colors.blue,
+
+            'circle' : {
+                strokeStyle: colors.blue,
+                lineWidth: 1,
+                fillStyle: colors.blue,
+                angleIndicator: colors.white
+            },
+
+            'rectangle' : {
+                strokeStyle: colors.violet,
+                lineWidth: 1,
+                fillStyle: colors.violet,
+                angleIndicator: colors.white
+            },
+
+            'convex-polygon' : {
+                strokeStyle: colors.violet,
+                lineWidth: 1,
+                fillStyle: colors.violet,
+                angleIndicator: colors.white
+            }
+        },
+        offset: { x: 0, y: 0 }
+    };
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            var self = this;
+
+            // call proto init
+            proto.init.call(this, options);
+
+            // further options
+            this.options.defaults( defaults, true );
+            this.options.onChange(function(){
+                self.options.offset = new Physics.vector( self.options.offset );
+            });
+            this.options( options, true );
+
+            // hidden canvas
+            this.hiddenCanvas = document.createElement('canvas');
+            this.hiddenCanvas.width = this.hiddenCanvas.height = 100;
+
+            if (!this.hiddenCanvas.getContext){
+                throw "Canvas not supported";
+            }
+
+            this.hiddenCtx = this.hiddenCanvas.getContext('2d');
+
+            // actual viewport
+            var viewport = this.el;
+            if (viewport.nodeName.toUpperCase() !== 'CANVAS'){
+
+                viewport = document.createElement('canvas');
+                this.el.appendChild( viewport );
+                if (typeof this.options.el === 'string' && this.el === document.body){
+                    viewport.id = this.options.el;
+                }
+                this.el = viewport;
+            }
+
+            this.container = this.el.parentNode;
+            this.ctx = viewport.getContext('2d');
+
+            this.els = {};
+
+            if (this.options.meta){
+                var stats = this.options.metaEl || newEl();
+                stats.className = 'pjs-meta';
+                this.els.fps = newEl('span');
+                this.els.ipf = newEl('span');
+                stats.appendChild(newEl('span', 'fps: '));
+                stats.appendChild(this.els.fps);
+                stats.appendChild(newEl('br'));
+                stats.appendChild(newEl('span', 'ipf: '));
+                stats.appendChild(this.els.ipf);
+
+                viewport.parentNode.insertBefore(stats, viewport);
+            }
+
+            this._layers = {};
+            this.addLayer( 'main', this.el );
+
+            if ( this.options.autoResize ){
+                this.resize();
+            } else {
+                this.resize( this.options.width, this.options.height );
+            }
+        },
+
+        /**
+         * CanvasRenderer#layer( id ) -> Layer
+         * - id (String): The id for the layer
+         *
+         * Get the layer by id.
+         **/
+        layer: function( id ){
+
+            if ( id in this._layers ){
+                return this._layers[ id ];
+            }
+
+            return null;
+        },
+
+        /**
+         * CanvasRenderer#addLayer( id[, el, opts ] ) -> Layer
+         * - id (String): The id for the layer
+         * - el (HTMLElement): The canvas element to use for this layer
+         * - opts (Object): The options for this layer (see below)
+         *
+         * Create a new layer.
+         *
+         * Layers can have the following options:
+         *
+         * - width: The width
+         * - height: The height
+         * - manual: Draw manually (default: `false`)
+         * - autoResize: Automatically resize the layer when the renderer's [[CanvasRenderer#resize]] method is called. (default: `true`)
+         * - follow: A [[Body]]. Offset this layer's rendering to follow a body's position. (default: `null`)
+         * - offset: The offset [[Vectorish]] for this layer. (default: `null`)
+         * - scale: Scale the layer by this amount. (default: `1`)
+         * - zIndex: The zIndex for the layer's HTMLElement. (default: `1`)
+         **/
+        addLayer: function( id, el, opts ){
+
+            /** belongs to: CanvasRenderer
+             * class Layer
+             *
+             * A rendering layer for the canvas renderer.
+             *
+             * Create by calling [[CanvasRenderer#addLayer]].
+             **/
+
+            var self = this
+                ,bodies = []
+                ,styles = Physics.util.extend({}, this.options.styles)
+                ,layer = {
+                    /**
+                     * Layer#id = String
+                     *
+                     * The layer's ID
+                     **/
+                    id: id
+                    /**
+                     * Layer#el = HTMLElement
+                     *
+                     * The layer's Canvas
+                     **/
+                    ,el: el || document.createElement('canvas')
+                    /** related to: Physics.util.options
+                      * Layer#options( options ) -> Object
+                      * - options (Object): The options to set as an object
+                      * + (Object): The options
+                      *
+                      * Set options on this layer.
+                      *
+                      * Access options directly from the options object.
+                      *
+                      * Example:
+                      *
+                      * ```javascript
+                      * this.options.someOption;
+                      * ```
+                      **/
+                    ,options: Physics.util.options({
+                        width: this.el.width
+                        ,height: this.el.height
+                        ,manual: false
+                        ,autoResize: true
+                        ,follow: null
+                        ,offset: null
+                        ,scale: 1
+                        ,zIndex: 1
+                    })( opts )
+                }
+                ;
+
+            if ( id in this._layers ){
+                throw 'Layer "' + id + '" already added.';
+            }
+
+            this.el.parentNode.insertBefore( layer.el, this.el );
+            layer.el.style.position = 'absolute';
+            layer.el.style.zIndex = layer.options.zIndex;
+            layer.el.className += ' pjs-layer-' + layer.id;
+            layer.ctx = layer.el.getContext('2d');
+            layer.ctx.scale( 1, 1 );
+            layer.el.width = layer.options.width;
+            layer.el.height = layer.options.height;
+
+            /**
+             * Layer#bodies = Array
+             *
+             * The Bodies this layer is rendering.
+             *
+             * The "main" layer will render all world bodies if it's empty.
+             **/
+            layer.bodies = bodies;
+
+            /**
+             * Layer#reset( [arr] ) -> this
+             * - arr (Array): Array to replace the current stack of Bodies.
+             *
+             * Reset the stack.
+             **/
+            layer.reset = function( arr ){
+
+                bodies = arr || [];
+                return layer;
+            };
+
+            /**
+             * Layer#addToStack( arr ) -> this
+             * Layer#addToStack( body ) -> this
+             * - body (Body): Body to add
+             * - arr (Array): Array of bodies to add
+             *
+             * Add body (bodies) to the rendering stack for this layer.
+             *
+             * Bodies must be added to the stack in order to be rendered by this layer UNLESS it is the "main" layer.
+             **/
+            layer.addToStack = function( thing ){
+
+                if ( Physics.util.isArray( thing ) ){
+                    bodies.push.apply( bodies, thing );
+                } else {
+                    bodies.push( thing );
+                }
+                return layer;
+            };
+
+            /**
+             * Layer#removeFromStack( arr ) -> this
+             * Layer#removeFromStack( body ) -> this
+             * - body (Body): Body to remove
+             * - arr (Array): Array of bodies to remove
+             *
+             * Remove body (bodies) from the rendering stack for this layer.
+             **/
+            layer.removeFromStack = function( thing ){
+
+                var i, l;
+
+                if ( Physics.util.isArray( thing ) ){
+                    for ( i = 0, l = thing.length; i < l; ++i ){
+                        layer.removeFromStack(thing[ i ]);
+                    }
+                } else {
+                    i = Physics.util.indexOf( bodies, thing );
+                    if ( i > -1 ){
+                        bodies.splice( i, 1 );
+                    }
+                }
+                return layer;
+            };
+
+            /**
+             * Layer#render( [clear] ) -> this
+             * - clear (Boolean): Clear the canvas (default: `true`)
+             *
+             * Render the bodies in this layer's stack.
+             *
+             * If you want you can replace this function with your own to do custom rendering.
+             *
+             * Example:
+             *
+             * ```javascript
+             * layer.render = myCustomRenderFn;
+             * ```
+             **/
+            layer.render = function( clear ){
+
+                var body
+                    ,scratch = Physics.scratchpad()
+                    ,offset = scratch.vector().set(0, 0)
+                    ,scale = layer.options.scale
+                    ,view
+                    ,i
+                    ,l = bodies.length
+                    ,t = self._interpolateTime
+                    ,stack = (l || layer.id !== 'main') ? bodies : self._world._bodies
+                    ;
+
+                if ( layer.options.manual ){
+                    scratch.done();
+                    return layer;
+                }
+
+                if ( layer.options.offset ){
+                    if ( layer.options.offset === 'center' ){
+                        offset.add( layer.el.width * 0.5, layer.el.height * 0.5 ).mult( 1/scale );
+                    } else {
+                        offset.vadd( layer.options.offset ).mult( 1/scale );
+                    }
+                }
+
+                if ( layer.options.follow ){
+                    offset.vsub( layer.options.follow.state.pos );
+                    offset.sub( layer.options.follow.state.vel.get(0)*t, layer.options.follow.state.vel.get(1)*t );
+                }
+
+                if ( clear !== false ){
+                    layer.ctx.clearRect(0, 0, layer.el.width, layer.el.height);
+                }
+
+                if ( scale !== 1 ){
+                    layer.ctx.save();
+                    layer.ctx.scale( scale, scale );
+                }
+
+                for ( i = 0, l = stack.length; i < l; ++i ){
+
+                    body = stack[ i ];
+                    if ( !body.hidden ){
+                        view = body.view || ( body.view = self.createView(body.geometry, body.styles || styles[ body.geometry.name ]) );
+                        self.drawBody( body, body.view, layer.ctx, offset );
+                    }
+                }
+
+                if ( scale !== 1 ){
+                    layer.ctx.restore();
+                }
+
+                scratch.done();
+                return layer;
+            };
+
+            // remember layer
+            this._layers[ id ] = layer;
+
+            return layer;
+        },
+
+        /**
+         * CanvasRenderer#removeLayer( id ) -> this
+         * CanvasRenderer#removeLayer( layer ) -> this
+         * - id (String): The id for the layer
+         * - layer (Layer): The layer
+         *
+         * Remove a layer.
+         **/
+        removeLayer: function( idOrLayer ){
+
+            var id = idOrLayer.id ? idOrLayer.id : idOrLayer
+                ,el = this._layers[ id ].el
+                ;
+
+            if ( el !== this.el ){
+                el.parentNode.removeChild( el );
+            }
+            delete this._layers[ id ];
+            return this;
+        },
+
+        /**
+         * CanvasRenderer#resize( width, height ) -> this
+         * - width (Number): The width
+         * - height (Number): The height
+         *
+         * Resize all layer canvases that have the `autoResize` option set to `true`.
+         **/
+        resize: function( width, height ){
+
+            var layer;
+            proto.resize.call( this, width, height );
+
+            for ( var id in this._layers ){
+
+                layer = this._layers[ id ];
+                if ( layer.options.autoResize ){
+                    layer.el.width = this.width;
+                    layer.el.height = this.height;
+                }
+            }
+
+            return this;
+        },
+
+        /**
+         * CanvasRenderer#setStyle( styles[, ctx] )
+         * - styles (Object|String): Styles to set on the canvas context
+         * - ctx (Canvas2DContext): The canvas context
+         *
+         * Set styles on the specified canvas context (or main context).
+         **/
+        setStyle: function( styles, ctx ){
+
+            ctx = ctx || this.ctx;
+
+            if ( Physics.util.isObject(styles) ){
+
+                styles.strokeStyle = styles.lineWidth ? styles.strokeStyle : 'rgba(0,0,0,0)';
+                Physics.util.extend(ctx, styles);
+
+            } else {
+
+                ctx.fillStyle = ctx.strokeStyle = styles;
+                ctx.lineWidth = 1;
+            }
+        },
+
+        /**
+         * CanvasRenderer#drawCircle( x, y, r, styles[, ctx] )
+         * - x (Number): The x coord
+         * - y (Number): The y coord
+         * - r (Number): The circle radius
+         * - styles (Object): The styles configuration
+         * - ctx (Canvas2DContext): The canvas context
+         *
+         * Draw a circle to specified canvas context.
+         **/
+        drawCircle: function(x, y, r, styles, ctx){
+
+            ctx = ctx || this.ctx;
+
+            ctx.beginPath();
+            this.setStyle( styles, ctx );
+            ctx.arc(x, y, r, 0, Pi2, false);
+            ctx.closePath();
+            ctx.stroke();
+            ctx.fill();
+        },
+
+        /**
+         * CanvasRenderer#drawPolygon( verts, styles[, ctx] )
+         * - verts (Array): Array of [[Vectorish]] vertices
+         * - styles (Object): The styles configuration
+         * - ctx (Canvas2DContext): The canvas context
+         *
+         * Draw a polygon to specified canvas context.
+         **/
+        drawPolygon: function(verts, styles, ctx){
+
+            var vert = verts[0]
+                ,x = vert.x
+                ,y = vert.y
+                ,l = verts.length
+                ;
+
+            ctx = ctx || this.ctx;
+            ctx.beginPath();
+            this.setStyle( styles, ctx );
+
+            ctx.moveTo(x, y);
+
+            for ( var i = 1; i < l; ++i ){
+
+                vert = verts[ i ];
+                x = vert.x;
+                y = vert.y;
+                ctx.lineTo(x, y);
+            }
+
+            if ( l > 2 ){
+                ctx.closePath();
+            }
+
+            ctx.stroke();
+            ctx.fill();
+        },
+
+        /**
+         * CanvasRenderer#drawRect( x, y, width, height, styles[, ctx] )
+         * - x (Number): The x coord
+         * - y (Number): The y coord
+         * - width (Number): The width
+         * - height (Number): The height
+         * - styles (Object): The styles configuration
+         * - ctx (Canvas2DContext): The canvas context
+         *
+         * Draw a rectangle to specified canvas context.
+         **/
+        drawRect: function(x, y, width, height, styles, ctx){
+
+            var hw = width * 0.5
+                ,hh = height * 0.5
+                ;
+
+            ctx = ctx || this.ctx;
+            this.setStyle( styles, ctx );
+            ctx.beginPath();
+            ctx.rect(x - hw, y - hh, width, height);
+            ctx.closePath();
+            ctx.stroke();
+            ctx.fill();
+        },
+
+        /**
+         * CanvasRenderer#drawLine( from, to, styles[, ctx] )
+         * - from (Vectorish): The starting pt
+         * - to (Vectorish): The ending pt
+         * - styles (Object): The styles configuration
+         * - ctx (Canvas2DContext): The canvas context
+         *
+         * Draw a line onto specified canvas context.
+         **/
+        drawLine: function(from, to, styles, ctx){
+
+            var x = from.x
+                ,y = from.y
+                ;
+
+            ctx = ctx || this.ctx;
+
+            ctx.beginPath();
+            this.setStyle( styles, ctx );
+
+            ctx.moveTo(x, y);
+
+            x = to.x;
+            y = to.y;
+
+            ctx.lineTo(x, y);
+
+            ctx.stroke();
+            ctx.fill();
+        },
+
+        /**
+         * CanvasRenderer#draw( geometry[, styles, ctx, offset] ) -> this
+         * - geometry (Geometry): The shape to draw
+         * - styles (Object): The styles configuration
+         * - ctx (Canvas2DContext): The canvas context
+         * - offset (Vectorish): The offset from center
+         *
+         * Draw a geometry to a context.
+         **/
+        draw: function( geometry, styles, ctx, offset ){
+
+            var name = geometry.name
+                ,x = +(offset && offset.x)
+                ,y = +(offset && offset.y)
+                ,w = geometry.aabb().hw
+                ;
+
+            ctx = ctx || this.ctx;
+            styles = styles || this.options.styles[ name ] || this.options.styles.circle || {};
+
+            ctx.save();
+            ctx.translate(x, y);
+
+            if (name === 'circle'){
+
+                this.drawCircle(0, 0, geometry.radius, styles, ctx);
+
+            } else if (name === 'convex-polygon'){
+
+                this.drawPolygon(geometry.vertices, styles, ctx);
+
+            } else if (name === 'rectangle'){
+
+                this.drawRect(0, 0, geometry.width, geometry.height, styles, ctx);
+
+            } else if (name === 'compound'){
+
+                for ( var i = 0, l = geometry.children.length, ch; i < l; i++ ){
+                    ch = geometry.children[ i ];
+
+                    // translate
+                    ctx.translate(ch.pos.x, ch.pos.y);
+                    // rotate
+                    ctx.rotate(ch.angle);
+
+                    this.draw( ch.g, styles, ctx );
+
+                    // unrotate
+                    ctx.rotate(-ch.angle);
+                    // untranslate
+                    ctx.translate(-ch.pos.x, -ch.pos.y);
+                }
+
+            } else {
+
+                // assume it's a point
+                this.drawCircle(0, 0, 1, styles, ctx);
+            }
+
+            if (name !== 'compound' && styles.angleIndicator){
+
+                ctx.beginPath();
+                this.setStyle( styles.angleIndicator, ctx );
+                ctx.moveTo(0, 0);
+                ctx.lineTo(w, 0);
+                ctx.closePath();
+                ctx.stroke();
+            }
+
+            ctx.restore();
+
+            return this;
+        },
+
+        // extended
+        createView: function( geometry, styles ){
+
+            var view
+                ,aabb = geometry.aabb()
+                ,hw = aabb.hw + Math.abs(aabb.x)
+                ,hh = aabb.hh + Math.abs(aabb.y)
+                ,offset = { x: hw + 1, y: hh + 1 }
+                ,hiddenCtx = this.hiddenCtx
+                ,hiddenCanvas = this.hiddenCanvas
+                ;
+
+            styles = styles || this.options.styles[ name ] || this.options.styles.circle || {};
+
+            // must want an image
+            if ( styles.src ){
+                view = new Image();
+                view.src = styles.src;
+                if ( styles.width ){
+                    view.width = styles.width;
+                }
+                if ( styles.height ){
+                    view.height = styles.height;
+                }
+                return view;
+            }
+
+            offset.x += styles.lineWidth | 0;
+            offset.y += styles.lineWidth | 0;
+
+            // clear and resize
+            hiddenCanvas.width = 2 * hw + 2 + (2 * styles.lineWidth|0);
+            hiddenCanvas.height = 2 * hh + 2 + (2 * styles.lineWidth|0);
+
+            this.draw( geometry, styles, hiddenCtx, offset );
+
+            view = new Image( hiddenCanvas.width, hiddenCanvas.height );
+            view.src = hiddenCanvas.toDataURL('image/png');
+            return view;
+        },
+
+        // extended
+        drawMeta: function( meta ){
+
+            this.els.fps.innerHTML = meta.fps.toFixed(2);
+            this.els.ipf.innerHTML = meta.ipf;
+        },
+
+        // extended
+        drawBody: function( body, view, ctx, offset ){
+
+            var pos = body.state.pos
+                ,os = body.offset
+                ,v = body.state.vel
+                ,t = this._interpolateTime || 0
+                ,x
+                ,y
+                ,ang
+                ,aabb
+                ;
+
+            offset = offset || this.options.offset;
+            ctx = ctx || this.ctx;
+
+            // interpolate positions
+            x = pos._[0] + offset.x + v._[0] * t;
+            y = pos._[1] + offset.y + v._[1] * t;
+            ang = body.state.angular.pos + body.state.angular.vel * t;
+
+            ctx.save();
+            ctx.translate( x, y );
+            ctx.rotate( ang );
+            ctx.translate( os._[0], os._[1] );
+            ctx.drawImage(view, -view.width/2, -view.height/2, view.width, view.height);
+            ctx.restore();
+        },
+
+        // extended
+        render: function( bodies, meta ){
+
+            var body
+                ,view
+                ,pos
+                ;
+
+            this._world.emit('beforeRender', {
+                renderer: this,
+                meta: meta
+            });
+
+            if ( this.options.meta ) {
+                this.drawMeta( meta );
+            }
+
+            this._interpolateTime = meta.interpolateTime;
+
+            for ( var id in this._layers ){
+
+                this._layers[ id ].render();
+            }
+
+            return this;
+        }
+    };
+});
+
+
+// ---
+// inside: src/renderers/dom.js
+
+/**
+ * class DomRenderer < Renderer
+ *
+ * Physics.renderer('dom')
+ *
+ * Renderer that manipulates DOM elements according to the physics simulation. Very primative...
+ **/
+Physics.renderer('dom', function( proto ){
+
+    if ( !document ){
+        // must be in node environment
+        return {};
+    }
+
+    // utility methods
+    var thePrefix = {}
+        ,tmpdiv = document.createElement("div")
+        ,toTitleCase = function toTitleCase(str) {
+            return str.replace(/(?:^|\s)\w/g, function(match) {
+                return match.toUpperCase();
+            });
+        }
+        // return the prefixed name for the specified css property
+        ,pfx = function pfx(prop) {
+
+            if (thePrefix[prop]){
+                return thePrefix[prop];
+            }
+
+            var arrayOfPrefixes = ['Webkit', 'Moz', 'Ms', 'O']
+                ,name
+                ;
+
+            for (var i = 0, l = arrayOfPrefixes.length; i < l; ++i) {
+
+                name = arrayOfPrefixes[i] + toTitleCase(prop);
+
+                if (name in tmpdiv.style){
+                    return thePrefix[prop] = name;
+                }
+            }
+
+            if (name in tmpdiv.style){
+                return thePrefix[prop] = prop;
+            }
+
+            return false;
+        }
+        ;
+
+    var classpfx = 'pjs-'
+        ,px = 'px'
+        ,cssTransform = pfx('transform')
+        ,borderRadius = pfx('borderRadius')
+        ;
+
+    var newEl = function( node, content ){
+            var el = document.createElement(node || 'div');
+            if (content){
+                el.innerHTML = content;
+            }
+            return el;
+        }
+        ,drawBody
+        ;
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            // call proto init
+            proto.init.call(this, options);
+
+            var viewport = this.el;
+            viewport.style.position = 'relative';
+            viewport.style.overflow = 'hidden';
+            viewport.style[cssTransform] = 'translateZ(0)'; // force GPU accel
+            viewport.style.width = this.options.width + px;
+            viewport.style.height = this.options.height + px;
+
+            this.els = {};
+
+            if (options.meta){
+                var stats = newEl();
+                stats.className = 'pjs-meta';
+                this.els.fps = newEl('span');
+                this.els.ipf = newEl('span');
+                stats.appendChild(newEl('span', 'fps: '));
+                stats.appendChild(this.els.fps);
+                stats.appendChild(newEl('br'));
+                stats.appendChild(newEl('span', 'ipf: '));
+                stats.appendChild(this.els.ipf);
+
+                viewport.appendChild(stats);
+            }
+
+            if ( this.options.autoResize ){
+                this.resize();
+            } else {
+                this.resize( this.options.width, this.options.height );
+            }
+        },
+
+        // extended
+        resize: function( width, height ){
+
+            proto.resize.call( this, width, height );
+            this.el.style.width = this.width + px;
+            this.el.style.height = this.height + px;
+        },
+
+        /** internal
+         * DomRenderer#pointProperties( el, geometry )
+         * - el (HTMLElement): The element
+         * - geometry (Geometry): The body's geometry
+         *
+         * Set dom element style properties for a point.
+         **/
+        pointProperties: function( el, geometry ){
+
+            el.style.width = '2px';
+            el.style.height = '2px';
+            el.style.marginLeft = '-1px';
+            el.style.marginTop = '-1px';
+            el.style[ borderRadius ] = '50%';
+        },
+
+        /** internal
+         * DomRenderer#circleProperties( el, geometry )
+         * - el (HTMLElement): The element
+         * - geometry (Geometry): The body's geometry
+         *
+         * Set dom element style properties for a circle.
+         **/
+        circleProperties: function( el, geometry ){
+
+            var aabb = geometry.aabb();
+
+            el.style.width = (aabb.hw * 2) + px;
+            el.style.height = (aabb.hh * 2) + px;
+            el.style.marginLeft = (-aabb.hw) + px;
+            el.style.marginTop = (-aabb.hh) + px;
+            el.style[ borderRadius ] = '50%';
+        },
+
+        /** internal
+         * DomRenderer#rectangleProperties( el, geometry )
+         * - el (HTMLElement): The element
+         * - geometry (Geometry): The body's geometry
+         *
+         * Set dom element style properties for a rectangle.
+         **/
+        rectangleProperties: function( el, geometry ){
+
+            var aabb = geometry.aabb();
+
+            el.style.width = (aabb.hw * 2) + px;
+            el.style.height = (aabb.hh * 2) + px;
+            el.style.marginLeft = (-aabb.hw) + px;
+            el.style.marginTop = (-aabb.hh) + px;
+        },
+
+        // extended
+        createView: function( geometry ){
+
+            var el = newEl()
+                ,chel
+                ,fn = geometry.name + 'Properties'
+                ;
+
+            el.className = classpfx + geometry.name;
+            el.style.position = 'absolute';
+            el.style.top = '0px';
+            el.style.left = '0px';
+
+            if ( geometry.name === 'compound' ){
+
+                for ( var i = 0, l = geometry.children.length, ch; i < l; i++ ){
+                    ch = geometry.children[ i ];
+                    chel = newEl();
+                    chel.className = classpfx + geometry.name + ' ' + classpfx + 'child';
+                    chel.style.position = 'absolute';
+                    chel.style.top = '0px';
+                    chel.style.left = '0px';
+                    if ( this[ ch.g.name + 'Properties' ] ){
+                        this[ ch.g.name + 'Properties' ](chel, ch.g);
+                    }
+                    chel.style[cssTransform] = 'translate('+ch.pos._[0]+'px,'+ch.pos._[1]+'px) rotate('+ ch.angle +'rad)';
+                    el.appendChild( chel );
+                }
+
+            } else if ( this[ fn ] ){
+                this[ fn ](el, geometry);
+            }
+
+            this.el.appendChild( el );
+            return el;
+        },
+
+        // extended
+        connect: function( world ){
+
+            world.on( 'add:body', this.attach, this );
+            world.on( 'remove:body', this.detach, this );
+        },
+
+        // extended
+        disconnect: function( world ){
+
+            world.off( 'add:body', this.attach, this );
+            world.off( 'remove:body', this.detach, this );
+        },
+
+        /**
+         * DomRenderer#detach( data ) -> this
+         * - data (HTMLElement|Object): DOM node or event data (`data.body`)
+         *
+         * Event callback to detach a node from the DOM
+         **/
+        detach: function( data ){
+
+            // interpred data as either dom node or event data
+            var el = (data.nodeType && data) || (data.body && data.body.view)
+                ,par = el && el.parentNode
+                ;
+
+            if ( el && par ){
+                // remove view from dom
+                par.removeChild( el );
+            }
+
+            return this;
+        },
+
+        /**
+         * DomRenderer#attach( data ) -> this
+         * - data (HTMLElement|Object): DOM node or event data (`data.body`)
+         *
+         * Event callback to attach a node to the viewport
+         **/
+        attach: function( data ){
+
+            // interpred data as either dom node or event data
+            var el = (data.nodeType && data) || (data.body && data.body.view)
+                ;
+
+            if ( el ){
+                // attach to viewport
+                this.el.appendChild( el );
+            }
+
+            return this;
+        },
+
+        // extended
+        drawMeta: function( meta ){
+
+            this.els.fps.innerHTML = meta.fps.toFixed(2);
+            this.els.ipf.innerHTML = meta.ipf;
+        },
+
+        // extended
+        drawBody: function( body, view ){
+
+            var pos = body.state.pos
+                ,v = body.state.vel
+                ,os = body.offset
+                ,x
+                ,y
+                ,ang
+                ,t = this._interpolateTime
+                ;
+
+            // interpolate positions
+            x = pos._[0] + v._[0] * t;
+            y = pos._[1] + v._[1] * t;
+            ang = body.state.angular.pos + body.state.angular.vel * t;
+            view.style[cssTransform] = 'translate('+x+'px,'+y+'px) rotate('+ ang +'rad) translate('+os._[0]+'px,'+os._[1]+'px)';
+        }
+    };
+});
+
+
+// ---
+// inside: src/renderers/pixi-renderer.js
+
+/*
+ * @requires pixi.js
+ */
+/**
+ * class PixiRenderer < Renderer
+ *
+ * Physics.renderer('pixi')
+ *
+ * Renderer that uses the PIXI.js library. [Documentation can be found here](https://github.com/wellcaffeinated/PhysicsJS/wiki/PIXI-Renderer).
+ *
+ * Additional config options:
+ *
+ * - metaEl: HTMLElement to write meta information like FPS and IPF into. (default: autogenerated)
+ * - offset: Offset the shapes by this amount. (default: `{ x: 0, y: 0 }`)
+ * - styles: Styles to use to draw the shapes. (see below)
+ *
+ * The styles property should contain _default_ styles for each shape you want to draw.
+ *
+ * Example:
+ *
+ * ```javascript
+ * styles: {
+ *    // Defines the default canvas colour
+ *    'color': '0x66FF99',
+ *
+ *    'circle' : {
+ *        strokeStyle: '0xE8900C',
+ *        lineWidth: 3,
+ *        fillStyle: '0xD5DE4C',
+ *        angleIndicator: '0xE8900C',
+ *        strokeAlpha: 1,
+ *        fillAlpha: 1
+ *    },
+ *
+ *    'convex-polygon' : {
+ *        strokeStyle: '0xE8900C',
+ *        lineWidth: 3,
+ *        fillStyle: '0xD5DE4C',
+ *        angleIndicator: '0xE8900C'
+ *    }
+ * }
+ * ```
+ *
+ * Styles can also be defined on a per-body basis. Use the "styles" property for a body:
+ *
+ * Example:
+ *
+ * ```javascript
+ * Physics.body('circle', {
+ *     // ...
+ *     styles: {
+ *        strokeStyle: '0x542437',
+ *        lineWidth: 1,
+ *        fillStyle: '0x542437',
+ *        angleIndicator: '0xFFFFFF'
+ *    }
+ * });
+ * ```
+ *
+ * You can also define an image to use for a body:
+ *
+ * Example:
+ *
+ * ```javascript
+ * Physics.body('circle', {
+ *     // ...
+ *     styles: {
+ *        src: 'path/to/image.jpg',
+ *        width: 40,
+ *        height: 50,
+ *        anchor: { x: 0.5, y: 0.5 }
+ *    }
+ * });
+ * ```
+ **/
+/* global PIXI */
+Physics.renderer('pixi', function( parent ){
+
+    if ( !document ){
+        // must be in node environment
+        return {};
+    }
+
+    var Pi2 = Math.PI * 2
+        ,colors = {
+            white: '0xFFFFFF'
+            ,violet: '0x542437'
+            ,blue: '0x53777A'
+        }
+        ,fontStyles = {
+            font: "18px monospace",
+            fill: "black",
+            align: "left"
+        }
+
+        ,defaults = {
+
+            // the element to place meta data into
+            metaEl: null,
+            offset: { x: 0, y: 0 },
+            // Provide some default colours
+            styles: {
+                // Defines the default canvas colour
+                'color': false,
+
+                'point': colors.blue,
+
+                'circle' : {
+                    strokeStyle: colors.blue,
+                    lineWidth: 1,
+                    fillStyle: colors.blue,
+                    angleIndicator: colors.white,
+                    fillAlpha: 1,
+                    strokeAlpha: 1,
+                    alpha: 1
+                },
+
+                'rectangle' : {
+                    strokeStyle: colors.violet,
+                    lineWidth: 1,
+                    fillStyle: colors.violet,
+                    angleIndicator: colors.white,
+                    fillAlpha: 1,
+                    strokeAlpha: 1,
+                    alpha: 1
+                },
+
+                'convex-polygon' : {
+                    strokeStyle: colors.violet,
+                    lineWidth: 1,
+                    fillStyle: colors.violet,
+                    angleIndicator: colors.white,
+                    fillAlpha: 1,
+                    strokeAlpha: 1,
+                    alpha: 1
+                }
+            }
+        }
+        ;
+
+    return {
+
+        // extended
+        init: function( options ){
+
+            var self = this
+                ,el
+                ,isTransparent
+                ;
+
+            if (typeof PIXI === 'undefined') {
+                throw "PIXI not present - cannot continue";
+            }
+
+            // call parent init
+            parent.init.call(this, options);
+
+            // further options
+            this.options.defaults( defaults, true );
+            this.options.onChange(function(){
+                self.options.offset = new Physics.vector( self.options.offset );
+            });
+            this.options( options, true );
+
+            isTransparent = (!this.options.styles.color || this.options.styles.color === 'transparent');
+            // Hook in PIXI stage here
+            this.stage = new PIXI.Stage(this.options.styles.color);
+
+            // Create empty meta object for use later
+            this.meta = {};
+
+            el = (this.el && this.el.nodeName === 'CANVAS') ? el : null;
+            // add the renderer view element to the DOM according to its type
+            this.renderer = new PIXI.autoDetectRenderer(this.options.width, this.options.height, {
+                view: el,
+                transparent: isTransparent,
+                resolution: window.devicePixelRatio || 1
+            });
+
+            if ( !el ){
+                this.el = this.el || document.body;
+                // add to passed in element
+                this.el.appendChild( this.renderer.view );
+            }
+
+            if ( this.options.autoResize ){
+                this.resize();
+            } else {
+                this.resize( this.options.width, this.options.height );
+            }
+        },
+
+        // extended
+        resize: function( width, height ){
+
+            parent.resize.call( this, width, height );
+            this.renderer.resize( this.width, this.height );
+        },
+
+        // extended
+        connect: function( world ){
+
+            world.on( 'add:body', this.attach, this );
+            world.on( 'remove:body', this.detach, this );
+        },
+
+        // extended
+        disconnect: function( world ){
+
+            world.off( 'add:body', this.attach, this );
+            world.off( 'remove:body', this.detach, this );
+        },
+
+        /**
+         * PixiRenderer#detach( data ) -> this
+         * - data (PIXI.Graphics|Object): Graphics object or event data (`data.body`)
+         *
+         * Event callback to detach a child from the stage
+         **/
+        detach: function( data ){
+
+            // interpred data as either dom node or event data
+            var el = (data instanceof PIXI.Graphics && data) || (data.body && data.body.view);
+
+            if ( el ){
+                // remove view from dom
+                this.stage.removeChild( el );
+            }
+
+            return this;
+        },
+
+        /**
+         * PixiRenderer#attach( data ) -> this
+         * - data (PIXI.Graphics|Object): Graphics object or event data (`data.body`)
+         *
+         * Event callback to attach a child to the stage
+         **/
+        attach: function( data ){
+
+            // interpred data as either dom node or event data
+            var el = (data instanceof PIXI.Graphics && data) || (data.body && data.body.view);
+
+            if ( el ){
+                // attach to viewport
+                this.stage.addChild( el );
+            }
+
+            return this;
+        },
+
+        /**
+         * PixiRenderer#loadSpriteSheets( assetsToLoad, callback ) -> this
+         * - assetsToLoad (Array): Array of spritesheets to load
+         * - callback (Function): Function to call when loading is complete
+         *
+         * Loads textures defined in a spritesheet
+         **/
+        loadSpriteSheets: function( assetsToLoad, callback ){
+
+            if ( !Physics.util.isArray( assetsToLoad ) ) {
+                throw 'Spritesheets must be defined in arrays';
+            }
+
+            var self = this
+                ,loader = new PIXI.AssetLoader(assetsToLoad)
+                ;
+
+            // Start loading resources!
+            loader.load();
+
+            loader.on('onComplete', function(evt){
+                self.assetsLoaded = true;
+                callback();
+            });
+
+            return self;
+        },
+
+        /**
+         * PixiRenderer#drawBody( body, view )
+         * - body (Body): The body to draw
+         * - view (DisplayObject): The pixi display object
+         *
+         * Draw a PIXI.DisplayObject to the stage.
+         **/
+        drawBody: function( body, view ){
+            var pos = body.state.pos
+                ,v = body.state.vel
+                ,os = body.offset
+                ,t = this._interpolateTime || 0
+                ,x
+                ,y
+                ,ang
+                ;
+
+            // interpolate positions
+            x = pos._[0] + v._[0] * t;
+            y = pos._[1] + v._[1] * t;
+            ang = body.state.angular.pos + body.state.angular.vel * t;
+
+            view.position.set( x, y );
+            view.pivot.set( -os._[0], -os._[1] );
+            view.rotation = ang;
+        },
+
+        // extended
+        render: function( bodies, meta ){
+
+            parent.render.call(this, bodies, meta);
+            this.renderer.render(this.stage);
+        },
+
+        /**
+         * PixiRenderer#setStyles( graphics, styles ) -> PIXI.Graphics
+         * - graphics (PIXI.Graphics): The graphics object to set styles on
+         * - styles (Object): The styles configuration
+         * + (PIXI.Graphics): A graphic object
+         *
+         * Set styles on pixi graphics object
+         **/
+        setStyles: function( graphics, styles ){
+
+            if ( Physics.util.isObject(styles) ){
+
+                if ( styles.fillStyle && styles.fillStyle !== 'transparent' ){
+                    graphics.beginFill( styles.fillStyle );
+                    graphics.fillAlpha = styles.fillAlpha !== undefined ? styles.fillAlpha : 1;
+                } else {
+                    graphics.beginFill();
+                    graphics.fillAlpha = 0;
+                }
+
+                graphics.lineStyle( styles.lineWidth || 0, styles.strokeStyle, styles.strokeAlpha !== undefined ? styles.strokeAlpha : 1 );
+                graphics.alpha = styles.alpha !== undefined ? styles.alpha : 1;
+
+            } else {
+
+                if ( styles && styles !== 'transparent' ){
+                    graphics.beginFill( styles );
+                } else {
+                    graphics.beginFill();
+                    graphics.fillAlpha = 0;
+                }
+
+                graphics.lineStyle( 0 );
+            }
+
+            return graphics;
+        },
+
+        /**
+         * PixiRenderer#createCircle( x, y, r, styles ) -> PIXI.Graphics
+         * - x (Number): The x coord
+         * - y (Number): The y coord
+         * - r (Number): The circle radius
+         * - styles (Object): The styles configuration
+         * + (PIXI.Graphics): A graphic object representing a circle.
+         *
+         * Create a circle for use in PIXI stage
+         **/
+        createCircle: function( x, y, r, styles ){
+
+            var graphics = new PIXI.Graphics();
+            this.setStyles( graphics, styles );
+            graphics.drawCircle( x, y, r );
+            graphics.endFill();
+            return graphics;
+        },
+
+        /**
+         * PixiRenderer#createRect( x, y, r, styles ) -> PIXI.Graphics
+         * - x (Number): The x coord
+         * - y (Number): The y coord
+         * - width (Number): The rectangle width
+         * - height (Number): The rectangle height
+         * - styles (Object): The styles configuration
+         * + (PIXI.Graphics): A graphic object representing a circle.
+         *
+         * Create a rectangle for use in PIXI stage
+         **/
+        createRect: function( x, y, width, height, styles ){
+
+            var graphics = new PIXI.Graphics();
+            this.setStyles( graphics, styles );
+            graphics.drawRect( x, y, width, height );
+            graphics.endFill();
+            return graphics;
+        },
+
+        /**
+         * PixiRenderer#createPolygon( verts, styles ) -> PIXI.Graphics
+         * - verts (Array): Array of [[Vectorish]] vertices
+         * - styles (Object): The styles configuration
+         * + (PIXI.Graphics): A graphic object representing a polygon.
+         *
+         * Create a polygon for use in PIXI stage
+         **/
+        createPolygon: function( verts, styles ){
+
+            var vert = verts[0]
+                ,x = vert.x
+                ,y = vert.y
+                ,l = verts.length
+                ,start = {
+                    x: x
+                    ,y: y
+                }
+                ,graphics = new PIXI.Graphics()
+                ;
+
+            this.setStyles( graphics, styles );
+
+            graphics.moveTo(x, y);
+
+            for ( var i = 1; i < l; ++i ){
+
+                vert = verts[ i ];
+                x = vert.x;
+                y = vert.y;
+                graphics.lineTo(x, y);
+            }
+
+            if (l > 2){
+                graphics.lineTo(start.x, start.y);
+            }
+
+            graphics.endFill();
+            return graphics;
+        },
+
+        /**
+         * PixiRenderer#createLine( from, to, styles ) -> PIXI.Graphics
+         * - from (Vectorish): Starting point
+         * - to (Vectorish): Ending point
+         * - styles (Object): The styles configuration
+         * + (PIXI.Graphics): A graphic object representing a polygon.
+         *
+         * Create a line for use in PIXI stage
+         **/
+        createLine: function( from, to, styles ){
+
+            var x = from.x
+                ,y = from.y
+                ;
+
+            var graphics = new PIXI.Graphics();
+            this.setStyles( graphics, styles );
+
+            graphics.moveTo(x, y);
+
+            x = to.x;
+            y = to.y;
+
+            graphics.lineTo(x, y);
+
+            graphics.endFill();
+            return graphics;
+        },
+
+        // extended
+        createView: function( geometry, styles, parent ){
+
+            var view = null
+                ,aabb = geometry.aabb()
+                ,hw = aabb.hw + Math.abs(aabb.x)
+                ,hh = aabb.hh + Math.abs(aabb.y)
+                ,name = geometry.name
+                ;
+
+            parent = parent || this.stage;
+            styles = styles || this.options.styles[ name ] || this.options.styles.circle || {};
+
+            // must want an image
+            if ( styles.src ){
+                view = PIXI.Sprite.fromImage( styles.src );
+                view.anchor.set( 0.5, 0.5 );
+                if ( styles.anchor ) {
+                    view.anchor.x = styles.anchor.x;
+                    view.anchor.y = styles.anchor.y;
+                }
+                if ( styles.width ){
+                    view.width = styles.width;
+                }
+                if ( styles.height ){
+                    view.height = styles.height;
+                }
+                parent.addChild(view);
+                return view;
+            }
+
+            if (name === 'circle'){
+
+                view = this.createCircle(0, 0, geometry.radius, styles);
+
+            } else if (name === 'convex-polygon'){
+
+                view = this.createPolygon(geometry.vertices, styles);
+
+            } else if (name === 'rectangle'){
+
+                view = this.createRect(-geometry.width/2, -geometry.height/2, geometry.width, geometry.height, styles);
+            } else if (name === 'compound'){
+
+                view = new PIXI.Graphics();
+
+                for ( var i = 0, l = geometry.children.length, ch, chview; i < l; i++ ){
+                    ch = geometry.children[ i ];
+                    chview = this.createView( ch.g, styles, view );
+                    chview.position.set( ch.pos.x, ch.pos.y );
+                    chview.rotation = ch.angle;
+                }
+            } else {
+
+                // assume it's a point
+                view = this.createCircle(0, 0, 1, styles);
+            }
+
+            if ( name !== 'compound' && styles.angleIndicator && styles.angleIndicator !== 'transparent' ){
+
+                view.lineStyle( styles.lineWidth, styles.angleIndicator );
+                view.moveTo( 0, 0 );
+                view.lineTo( hw, 0 );
+            }
+
+            if ( name !== 'compound' ){
+                view.cacheAsBitmap = true;
+            }
+
+            parent.addChild(view);
+            return view;
+        },
+
+        // extended
+        drawMeta: function( meta ){
+            if (!this.meta.loaded){
+                // define the font styles here
+                this.meta.fps = new PIXI.Text('FPS: ' + meta.fps.toFixed(2), fontStyles);
+                this.meta.fps.position.x = 15;
+                this.meta.fps.position.y = 5;
+
+                this.meta.ipf = new PIXI.Text('IPF: ' + meta.ipf, fontStyles);
+                this.meta.ipf.position.x = 15;
+                this.meta.ipf.position.y = 30;
+
+                this.stage.addChild(this.meta.fps);
+                this.stage.addChild(this.meta.ipf);
+                this.meta.loaded = true;
+            } else {
+                this.meta.fps.setText('FPS: ' + meta.fps.toFixed(2));
+                this.meta.ipf.setText('IPF: ' + meta.ipf);
+            }
+        },
+
+        /**
+         * PixiRenderer#createDisplay( type, options ) -> PIXI.DisplayObject
+         * - type (String): The type of PIXI.DisplayObject to make
+         * - options (Object): Options to apply to the view.
+         * + (PIXI.DisplayObject): An object that is renderable.
+         *
+         * Create a PIXI sprite or movie clip.
+         **/
+        createDisplay: function( type, options ){
+            var view = null
+                ,texture = null
+                ;
+            switch (type){
+                // Create a sprite object
+                case 'sprite':
+                    texture = PIXI.Texture.fromImage(options.texture);
+                    view = new PIXI.Sprite(texture);
+                    if (options.anchor ) {
+                        view.anchor.x = options.anchor.x;
+                        view.anchor.y = options.anchor.y;
+                    }
+                    // If a container is specified, use add to that container
+                    if (options.container) {
+                        options.container.addChild(view);
+                    } else {
+                        // Otherwise just add the view to the stage
+                        this.stage.addChild(view);
+                    }
+                    return view;
+                // Create a movieclip object
+                case 'movieclip':
+                    if (!this.assetsLoaded) {
+                        throw "No assets have been loaded. Use loadSpritesheet() first";
+                    }
+                    var tex = []
+                        ,i = 0
+                        ;
+                    // Populate our movieclip
+                    for (i; i < options.frames.length; i++) {
+                        texture = PIXI.Texture.fromFrame(options.frames[i]);
+                        tex.push(texture);
+                    }
+                    view = new PIXI.MovieClip(tex);
+                    if (options.anchor ) {
+                        view.anchor.x = options.anchor.x;
+                        view.anchor.y = options.anchor.y;
+                    }
+                    // If a container is specified, use add to that container
+                    if (options.container) {
+                        options.container.addChild(view);
+                    } else {
+                        // Otherwise just add the view to the stage
+                        this.stage.addChild(view);
+                    }
+                    return view;
+                // Create a default case
+                default:
+                    throw 'Invalid PIXI.DisplayObject passed';
+            }
+        },
+
+        /**
+         * PixiRenderer#centerAnchor( view )
+         * - view (PIXI.DisplayObject): The view to center
+         *
+         * Centers the anchor to {x: 0.5, y: 0.5} of a view
+         **/
+        centerAnchor: function( view ) {
+            if (view !== null){
+                view.anchor.x = 0.5;
+                view.anchor.y = 0.5;
+            }
+        }
+    };
+});
+
+
+// ---
+// inside: src/outro.js
+
+return Physics;
+}));

+ 30 - 0
sites/all/modules/figli/edlp_corpus/assets/dist/scripts/corpus.min.js

@@ -0,0 +1,30 @@
+(function($) {
+
+
+  EdlpCorpus = function(){
+    var $container = $('body>div.layout-container');
+    var $canvas = $('<canvas>').addClass('edlp-map').appendTo($container);
+    var canvas = $canvas[0];
+    var ctx = canvas.getContext('2d');
+    var physics = new Physics();
+
+    function init(){
+      console.log("EdlpCorpus init()");
+      initMap();
+    };
+
+    function initMap(){
+      console.log("EdlpCorpus initMap()");
+      console.log('physics',physics);
+    }
+
+    init();
+  }
+
+
+  $(document).ready(function($) {
+    var edlpcorpus = new EdlpCorpus();
+  });
+
+
+})(jQuery);

+ 0 - 0
sites/all/modules/figli/edlp_corpus/assets/dist/styles/corpus.min.css


+ 30 - 0
sites/all/modules/figli/edlp_corpus/assets/scripts/corpus.js

@@ -0,0 +1,30 @@
+(function($) {
+
+
+  EdlpCorpus = function(){
+    var $container = $('body>div.layout-container');
+    var $canvas = $('<canvas>').addClass('edlp-map').appendTo($container);
+    var canvas = $canvas[0];
+    var ctx = canvas.getContext('2d');
+    var physics = new Physics();
+
+    function init(){
+      console.log("EdlpCorpus init()");
+      initMap();
+    };
+
+    function initMap(){
+      console.log("EdlpCorpus initMap()");
+      console.log('physics',physics);
+    }
+
+    init();
+  }
+
+
+  $(document).ready(function($) {
+    var edlpcorpus = new EdlpCorpus();
+  });
+
+
+})(jQuery);

+ 0 - 0
sites/all/modules/figli/edlp_corpus/assets/styles/corpus.scss


+ 19 - 0
sites/all/modules/figli/edlp_corpus/bower.json

@@ -0,0 +1,19 @@
+{
+  "name": "edlp_d8_corpus",
+  "version": "1.0.0",
+  "authors": [
+    "Bachir Soussi Chiadmi <bachir@figureslibres.io>"
+  ],
+  "license": "MIT",
+  "homepage": "http://encyclopediedelaparole.org",
+  "ignore": [
+    "**/.*",
+    "node_modules",
+    "bower_components",
+    "test",
+    "tests"
+  ],
+  "dependencies": {
+    "PhysicsJS": "latest"
+  }
+}

+ 9 - 0
sites/all/modules/figli/edlp_corpus/edlp_corpus.info.yml

@@ -0,0 +1,9 @@
+name: Edlp Corpus
+type: module
+description: Creates interactive map of corpus for edlp d8.
+core: 8.x
+package: Edlp
+# dependencies:
+#   - migrate_drupal
+#   - migrate_plus
+#   - migrate_tools

+ 12 - 0
sites/all/modules/figli/edlp_corpus/edlp_corpus.libraries.yml

@@ -0,0 +1,12 @@
+corpus:
+  version: VERSION
+  css:
+    base:
+      assets/dist/styles/corpus.min.css: {}
+
+  version: VERSION
+  js:
+    assets/dist/bower/physicsjs-full.js: { scope: footer }
+    assets/dist/scripts/corpus.min.js: { scope: footer }
+  dependencies:
+    - core/jquery

+ 11 - 0
sites/all/modules/figli/edlp_corpus/edlp_corpus.module

@@ -0,0 +1,11 @@
+<?php
+
+/**
+ * Implements hook_page_attachments().
+ * @param array $attachments
+ */
+function edlp_corpus_page_attachments(array &$attachments) {
+    //add here any conditions if you need to limit the pages
+
+    $attachments['#attached']['library'][] = 'edlp_corpus/corpus';
+}

+ 7 - 0
sites/all/modules/figli/edlp_corpus/edlp_corpus.routing.yml

@@ -0,0 +1,7 @@
+edlp_corpus.content:
+  path: '/edlp/corpus'
+  defaults:
+    _controller: '\Drupal\edlp_corpus\Controller\CorpusController::content'
+    _title: 'Corpus'
+  requirements:
+    _permission: 'access content'

+ 56 - 0
sites/all/modules/figli/edlp_corpus/gulpfile.js

@@ -0,0 +1,56 @@
+'use strict';
+
+var gulp = require('gulp');
+var util = require('gulp-util');
+var sass = require('gulp-sass');
+var watch = require('gulp-watch');
+var autoprefixer = require('gulp-autoprefixer');
+var jsmin = require('gulp-jsmin');
+var cssmin = require('gulp-cssmin');
+var rename = require('gulp-rename');
+var mainBowerFiles = require('main-bower-files');
+
+function handleError(err) {
+  console.log(err.toString());
+}
+
+var config = {
+  production: !!util.env.production
+}
+
+gulp.task('scripts', function () {
+    gulp.src('./assets/scripts/corpus.js')
+      .pipe(config.production ? jsmin() : util.noop())
+      .pipe(rename({suffix: '.min'}))
+      .pipe(gulp.dest('./assets/dist/scripts/'));
+});
+
+gulp.task('styles', function () {
+  gulp.src('./assets/styles/corpus.scss')
+    .pipe(sass().on('error', sass.logError))
+    .pipe(autoprefixer({
+        browsers: ['last 2 versions'],
+        cascade: false
+    })).on('error', handleError)
+    .pipe(config.production ? cssmin() : util.noop())
+    .pipe(rename({suffix: '.min'}))
+    .pipe(gulp.dest('./assets/dist/styles/'));
+});
+
+gulp.task('bower', function() {
+    gulp.src(mainBowerFiles({
+      "overrides":{
+        "jquery":{
+          "ignore":true
+        }
+      }
+    }))
+      .pipe(gulp.dest('./assets/dist/bower/'));
+});
+
+// default gulp task
+gulp.task('default', ['bower', 'scripts', 'styles'], function() {
+  gulp.watch('./assets/styles/*.scss', ['styles']);
+  // gulp.watch('./assets/styles/*/*.scss', ['styles']);
+  gulp.watch('./assets/scripts/*.js', ['scripts']);
+});

+ 22 - 0
sites/all/modules/figli/edlp_corpus/package.json

@@ -0,0 +1,22 @@
+{
+  "name": "edlpd8theme",
+  "version": "0.0.1",
+  "description": "A theme for drupal.",
+  "main": "gulpfile.js",
+  "author": "Bachir Soussi Chiadmi",
+  "license": "GPLv3",
+  "homepage": "https://encyclopediedelaparole.org",
+  "devDependencies": {
+    "gulp": "latest",
+    "gulp-autoprefixer": "latest",
+    "gulp-cssmin": "latest",
+    "gulp-jsmin": "latest",
+    "gulp-rename": "latest",
+    "gulp-sass": "latest",
+    "gulp-strip-debug": "latest",
+    "gulp-util": "latest",
+    "gulp-watch": "latest",
+    "main-bower-files": "^2.13.1"
+  },
+  "dependencies": {}
+}

+ 21 - 0
sites/all/modules/figli/edlp_corpus/src/Controller/CorpusController.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace Drupal\edlp_corpus\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+
+class CorpusController extends ControllerBase {
+
+  /**
+   * Display the markup.
+   *
+   * @return array
+   */
+  public function content() {
+    return array(
+      '#type' => 'markup',
+      '#markup' => $this->t('Hello, Edlp Corpus!'),
+    );
+  }
+
+}