     @cachedVertices ?= (Vec.transform offset, @position, @orientation for offset in @offsets)
 
 class Contact
-  constructor: (@position, @normal, @depth) ->
+  restitution: 1 # bounce!
+
+  constructor: (@from, @to, @position, @normal, @depth, restitution=null) ->
+    @restitution = restitution if restitution?
+
+  # Calculated fresh each time, as the position and velocity may have changed
+  # during a previous contact resolution iteration
+  separatingVelocity: ->
+    relativeV = @from.velocity
+    if @to
+      relativeV = Vec.sub relativeV, @to.velocity
+    Vec.dotProduct relativeV, @normal
+
+  resolve: ->
+    @resolveVelocity()
+    @resolveInterpenetration()
+
+  resolveVelocity: ->
+    sepV = @separatingVelocity()
+    return if sepV >= 0 # separating or stationary
+
+    newSepV = -sepV * @restitution
+    deltaV = newSepV - sepV
+
+    # apply change in velocity in proportion to inverse mass
+    totalInvMass = @totalInvMass()
+    return if totalInvMass <= 0 # nobody's goin' nowhere
+
+    impulse = deltaV / totalInvMass
+    impulsePerMass = Vec.scale @normal, impulse
+
+    # apply impulses
+    @from.velocity = Vec.add @from.velocity, Vec.scale impulsePerMass, @from.inverseMass
+    if @to
+      @to.velocity = Vec.add @to.velocity, Vec.scale impulsePerMass, @to.inverseMass
+
+  resolveInterpenetration: ->
+    return if @depth <= 0
+
+    totalInvMass = @totalInvMass()
+    return if totalInvMass <= 0 # nobody's goin' nowhere
+
+    movePerIMass = Vec.scale @normal, (@depth / totalInvMass)
+
+    @from.position = Vec.add @from.position, Vec.scale movePerIMass, @from.inverseMass
+    if @to
+      @to.position = Vec.add @to.position, Vec.scale movePerIMass, -@to.inverseMass
+
+  totalInvMass: ->
+    total = @from.inverseMass
+    total += @to.inverseMass if @to
+    total
 
 window.Rotation =
   fromAngle: (angle) -> [Math.cos(angle), Math.sin(angle)]

    for point in clipped
      depth = Vec.dotProduct(refNorm, point) - maxDepth
      if depth >= 0
        contacts.push new Contact(point, contactNormal, depth)

    # For simplicity sake, only return the "deepest" contact point. Eventually
    # the physics engine will need to track more than one contact and update

    clear: ->
      ctx.clearRect -ctx.width/2, -ctx.height/2, ctx.width, ctx.height
    keyup: (e) ->
      controls.keyup e
    keydown: controls.keydown

