                          {:dx (- (.-x e1-center) (.-x e2-center))
                           :dy (- (.-y e1-center) (.-y e2-center))}))))))
 
-(defn move-camera-on-pan! [current-state]
+(defn move-camera-on-pan! [manager current-state]
   (.subscribe
-    (pan-deltas-observable (:svg @current-state))
+    (pan-deltas-observable manager (:svg @current-state))
     (fn [d] (let [{:keys [dx dy]} d] (move-camera! current-state dx dy)))))
+
+;; Gestures - swipe
+
+(defn swipe-observable 
+  "A stream of velocity vectors, one per swipe."
+  [manager svg]
+  (.map (gesture-observable manager svg "swipe")
+        (fn [e] {:vx (.-velocityX e), :vy (.-velocityY e)})))
+
+(defn scroll-end-observable [svg]
+  (let [elem (.node svg)]
+    (-> rx-observable (.fromEvent elem "mousedown")
+        (.merge (-> rx-observable (.fromEvent elem "touchstart")))
+        (.map (constantly :scroll-end)))))
+
+(defn dampen [x y]
+  (let [length (dist x y)]
+    (vector-with-length (- length swipe-damping-factor) x y)))
+
+(defn swipe-displacements-observable [manager svg]
+  (let [swipes (swipe-observable manager svg)
+        scroll-ends (scroll-end-observable svg)
+        actions (.merge swipes scroll-ends)]
+    (.flatMapLatest
+      actions
+      (fn [a]
+        (if (= a :scroll-end)
+          (.empty rx-observable)
+          (let [{:keys [vx vy]} a]
+            (.generateWithRelativeTime rx-observable
+              {:dx (* update-interval-ms (or vx 0)), :dy (* update-interval-ms (or vy 0))}
+              (fn [d] 
+                (> (dist-sq (:dx d) (:dy d)) 10))
+              (fn [d] (let [[x y] (apply dampen (map d [:dx :dy]))] {:dx x :dy y}))
+              identity
+              (constantly update-interval-ms))))))))
+
+
+(defn move-camera-on-swipe! [manager current-state]
+  (.subscribe
+    (swipe-displacements-observable manager (:svg @current-state))
+    (fn [d] 
+      (let [{:keys [dx dy]} d] (move-camera! current-state dx dy)))))
 
 ;; Exported function to do magic
 

(defn hammer-manager [svg]
  (let [manager (js/Hammer.Manager. (.node svg))]
    (.add manager (js/Hammer.Pan.))
    manager))

(defn pan-observable [svg]
  (let [manager (hammer-manager svg)]
    (.create
      rx-observable
      (fn [observer]
        (.on manager "panstart panmove"
             (fn [e] (.onNext observer e)))))))

(defn pan-deltas-observable [svg]
  (-> (pan-observable svg)
      (.bufferWithCount 2 1)
      (.map (fn [es] (let [[e1 e2] es
                           e1-center (.-center e1)

  (swap-state! current-state #(-> %
                                  (update :camera-x + dx)
                                  (update :camera-y + dy))))

;; Math

(def sqrt (.-sqrt js/Math))

(defn dist-sq [x y]
  (+ (* x x) (* y y)))

(def dist (comp sqrt dist-sq))

(defn vector-with-length [l x y]
  (let [factor (/ l (dist x y))]
    [(* factor x) (* factor y)]))

;; Layout

