       switch k
         when "horizontal" then v.recognize ||= (e) -> e.delta.absoluteAspectRatio > 1
         when "vertical"   then v.recognize ||= (e) -> e.delta.absoluteAspectRatio < 1
+        when "rotate"
+          v.recognize ||= ({firstLocation, target, delta}) ->
+            startVector = firstLocation.sub target.currentSize.div(2)
+
+            # via dot-product
+            projectionTowardsCenterSquared = startVector.scalerProjectionSquared delta
+
+            # via Pythagoras
+            projectionTowardsRadialSquared = startVector.magnitudeSquared - projectionTowardsCenterSquared
+
+
+            recognized = projectionTowardsCenterSquared < projectionTowardsRadialSquared
+            # log {startVector, delta, projectionTowardsRadialSquared, projectionTowardsCenterSquared, recognized}
+            recognized
 
   @getter
     pointerHandlers: ->

      These 5 'normal' events are passed through IFF a gesture hasn't been recognized yet (or was never recognized).

    horizontal/vertical:
      begin: (e) ->
        IN: e: the original pointerDown PointerEvent
        When the gesture is recognized, this is called with the original pointerDown event.

        pointerDown:  (e) =>
          @_capturedEvents = false
          @_nonGestureHandlers.pointerDown? e
          @_nonGestureHandlers.prepare? e
          @_startEvent = if e.newEvent then e.newEvent() else clone e
          @_resumeGesture e if @_lastActiveGesture?.resume? e

