Adapt the application to use the new point abstraction.

This commit is contained in:
Andrey Antukh 2016-02-06 12:29:00 +02:00
parent b102c19ea7
commit 5d5b84a0a1
9 changed files with 126 additions and 105 deletions

View File

@ -10,6 +10,7 @@
[uxbox.time :as time] [uxbox.time :as time]
[uxbox.xforms :as xf] [uxbox.xforms :as xf]
[uxbox.shapes :as sh] [uxbox.shapes :as sh]
[uxbox.util.geom.point :as gpt]
[uxbox.util.data :refer (index-of)])) [uxbox.util.data :refer (index-of)]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -156,7 +157,8 @@
(defn move-shape (defn move-shape
"Mark a shape selected for drawing in the canvas." "Mark a shape selected for drawing in the canvas."
[sid [dx dy :as delta]] [sid delta]
{:pre [(gpt/point? delta)]}
(reify (reify
rs/UpdateEvent rs/UpdateEvent
(-apply-update [_ state] (-apply-update [_ state]

View File

@ -116,14 +116,16 @@
;; Resize ;; Resize
(defmethod -resize :builtin/line (defmethod -resize :builtin/line
[shape {:keys [x2 y2] :as pos}] [shape {:keys [x y] :as pos}]
(assoc shape (assoc shape
:x2 x2 :y2 y2)) :x2 x :y2 x))
(defmethod -resize :builtin/circle (defmethod -resize :builtin/circle
[{:keys [cx cy rx ry] :as shape} {:keys [x2 y2 lock] :as pos}] [{:keys [cx cy rx ry] :as shape} {:keys [x y lock] :as pos}]
(let [x1 (- cx rx) (let [x1 (- cx rx)
y1 (- cy ry) y1 (- cy ry)
x2 x
y2 y
width (- x2 x1) width (- x2 x1)
height (if lock height (if lock
@ -140,15 +142,14 @@
(assoc shape :rx rx :ry ry :cx cx :cy cy)))) (assoc shape :rx rx :ry ry :cx cx :cy cy))))
(defmethod -resize :builtin/rect (defmethod -resize :builtin/rect
[shape {:keys [x2 y2 lock] :as pos}] [shape {:keys [x y lock] :as pos}]
(let [{:keys [x y]} shape] (if lock
(if lock (assoc shape
(assoc shape :width (- x (:x shape))
:width (- x2 x) :height (- x (:x shape)))
:height (- x2 x)) (assoc shape
(assoc shape :width (- x (:x shape))
:width (- x2 x) :height (- y (:y shape)))))
:height (- y2 y)))))
(defmethod -resize :default (defmethod -resize :default
[shape _] [shape _]
@ -167,19 +168,19 @@
;; Move ;; Move
(defmethod -move ::rect (defmethod -move ::rect
[shape [dx dy]] [shape {dx :x dy :y}]
(assoc shape (assoc shape
:x (+ (:x shape) dx) :x (+ (:x shape) dx)
:y (+ (:y shape) dy))) :y (+ (:y shape) dy)))
(defmethod -move :builtin/group (defmethod -move :builtin/group
[shape [dx dy]] [shape {dx :x dy :y}]
(assoc shape (assoc shape
:dx (+ (:dx shape 0) dx) :dx (+ (:dx shape 0) dx)
:dy (+ (:dy shape 0) dy))) :dy (+ (:dy shape 0) dy)))
(defmethod -move :builtin/line (defmethod -move :builtin/line
[shape [dx dy]] [shape {dx :x dy :y}]
(assoc shape (assoc shape
:x1 (+ (:x1 shape) dx) :x1 (+ (:x1 shape) dx)
:y1 (+ (:y1 shape) dy) :y1 (+ (:y1 shape) dy)
@ -187,7 +188,7 @@
:y2 (+ (:y2 shape) dy))) :y2 (+ (:y2 shape) dy)))
(defmethod -move :builtin/circle (defmethod -move :builtin/circle
[shape [dx dy]] [shape {dx :x dy :y}]
(assoc shape (assoc shape
:cx (+ (:cx shape) dx) :cx (+ (:cx shape) dx)
:cy (+ (:cy shape) dy))) :cy (+ (:cy shape) dy)))

View File

@ -25,7 +25,7 @@
(defn- coordenates-render (defn- coordenates-render
[own] [own]
(let [[x y] (rum/react wb/mouse-position)] (let [{:keys [x y]} (rum/react wb/mouse-position)]
(html (html
[:div {:style {:position "absolute" :left "80px" :top "20px"}} [:div {:style {:position "absolute" :left "80px" :top "20px"}}
[:table [:table

View File

@ -6,6 +6,7 @@
[uxbox.state :as st] [uxbox.state :as st]
[uxbox.data.projects :as dp] [uxbox.data.projects :as dp]
[uxbox.data.workspace :as dw] [uxbox.data.workspace :as dw]
[uxbox.util.geom.point :as gpt]
[uxbox.util.lens :as ul] [uxbox.util.lens :as ul]
[goog.events :as events]) [goog.events :as events])
(:import goog.events.EventType)) (:import goog.events.EventType))
@ -51,25 +52,15 @@
;; Scroll Stream ;; Scroll Stream
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defonce ^:private scroll-b (rx/bus)) (defonce scroll-b (rx/bus))
(defonce scroll-s (defonce scroll-s
(as-> scroll-b $ (as-> scroll-b $
(rx/merge $ (rx/of {:top 0 :left 0})) (rx/merge $ (rx/of (gpt/point)))
(rx/dedupe $))) (rx/dedupe $)))
(defonce scroll-top-s (defonce scroll
(->> scroll-s (rx/to-atom scroll-s))
(rx/map :top)
(rx/dedupe)))
(defonce scroll-left-s
(->> scroll-s
(rx/map :left)
(rx/dedupe)))
(defonce scroll-top (rx/to-atom scroll-top-s))
(defonce scroll-left (rx/to-atom scroll-left-s))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Interactions ;; Interactions
@ -107,10 +98,7 @@
(defn- coords-delta (defn- coords-delta
[[old new]] [[old new]]
(let [[oldx oldy] old (gpt/subtract new old))
[newx newy] new]
[(- newx oldx)
(- newy oldy)]))
(defonce mouse-delta-s (defonce mouse-delta-s
(->> mouse-s (->> mouse-s
@ -135,4 +123,3 @@
(def document-start-x 50) (def document-start-x 50)
(def document-start-y 50) (def document-start-y 50)

View File

@ -14,6 +14,7 @@
[uxbox.data.projects :as dp] [uxbox.data.projects :as dp]
[uxbox.data.workspace :as dw] [uxbox.data.workspace :as dw]
[uxbox.ui.mixins :as mx] [uxbox.ui.mixins :as mx]
[uxbox.util.geom.point :as gpt]
[uxbox.util.dom :as dom] [uxbox.util.dom :as dom]
[uxbox.ui.keyboard :as kbd] [uxbox.ui.keyboard :as kbd]
[uxbox.ui.workspace.base :as wb] [uxbox.ui.workspace.base :as wb]
@ -109,19 +110,19 @@
(defn- canvas-did-mount (defn- canvas-did-mount
[own] [own]
(letfn [(on-mousemove [event page [offset-x offset-y]] (letfn [(on-mousemove [event page offset-pt]
(let [x (.-clientX event) (let [wpt (gpt/point (.-clientX event) (.-clientY event))
y (.-clientY event) cpt (gpt/subtract wpt offset-pt)
event {:id (:id page) event {:id (:id page)
:ctrl (kbd/ctrl? event) :ctrl (kbd/ctrl? event)
:window-coords [x y] :shift (kbd/shift? event)
:canvas-coords [(- x offset-x) :window-coords wpt
(- y offset-y)]}] :canvas-coords cpt}]
(rx/push! wb/mouse-b event)))] (rx/push! wb/mouse-b event)))]
(let [[page] (:rum/props own) (let [[page] (:rum/props own)
canvas (mx/get-ref-dom own (str "canvas" (:id page))) canvas (mx/get-ref-dom own (str "canvas" (:id page)))
brect (.getBoundingClientRect canvas) brect (.getBoundingClientRect canvas)
brect [(.-left brect) (.-top brect)] brect (gpt/point (.-left brect) (.-top brect))
key (events/listen js/document EventType.MOUSEMOVE key (events/listen js/document EventType.MOUSEMOVE
#(on-mousemove % page brect))] #(on-mousemove % page brect))]
(swap! wb/bounding-rect assoc (:id page) brect) (swap! wb/bounding-rect assoc (:id page) brect)

View File

@ -10,6 +10,7 @@
[uxbox.data.workspace :as dw] [uxbox.data.workspace :as dw]
[uxbox.ui.workspace.base :as wb] [uxbox.ui.workspace.base :as wb]
[uxbox.ui.mixins :as mx] [uxbox.ui.mixins :as mx]
[uxbox.util.geom.point :as gpt]
[uxbox.util.dom :as dom])) [uxbox.util.dom :as dom]))
(defonce +drawing-shape+ (atom nil)) (defonce +drawing-shape+ (atom nil))
@ -37,13 +38,13 @@
;; Subscriptions ;; Subscriptions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FIXME: this works for now, but should be refactored when advanced rendering
;; is introduced such as polyline, polygon and path.
(define-once :drawing-subscriptions (define-once :drawing-subscriptions
(letfn [(init-shape [shape] (letfn [(init-shape [shape]
(let [[x y :as mpos] @wb/mouse-position (let [{:keys [x y]} (gpt/subtract @wb/mouse-position @wb/scroll)
stop @wb/scroll-top
y (+ stop y)
shape (sh/-initialize shape {:x1 x :y1 y :x2 x :y2 y})] shape (sh/-initialize shape {:x1 x :y1 y :x2 x :y2 y})]
(reset! +drawing-shape+ shape) (reset! +drawing-shape+ shape)
(reset! +drawing-position+ {:x2 x :y2 y :lock false}) (reset! +drawing-position+ {:x2 x :y2 y :lock false})
@ -54,10 +55,9 @@
(rx/with-latest-from vector wb/mouse-ctrl-s $) (rx/with-latest-from vector wb/mouse-ctrl-s $)
(rx/subscribe $ on-value nil on-complete)))) (rx/subscribe $ on-value nil on-complete))))
(on-value [[[x y :as pos] ctrl?]] (on-value [[pos ctrl?]]
(let [stop @wb/scroll-top] (let [{:keys [x y] :as pos} (gpt/subtract pos @wb/scroll)]
(reset! +drawing-position+ (reset! +drawing-position+ {:x2 x :y2 y :lock ctrl?})))
{:x2 x :y2 (+ y stop) :lock ctrl?})))
(on-complete [] (on-complete []
(let [shape @+drawing-shape+ (let [shape @+drawing-shape+
@ -69,9 +69,7 @@
(reset! +drawing-shape+ nil))) (reset! +drawing-shape+ nil)))
(init-icon [shape] (init-icon [shape]
(let [[x y] @wb/mouse-position (let [{:keys [x y]} (gpt/subtract @wb/mouse-position @wb/scroll)
stop @wb/scroll-top
y (+ stop y)
props {:x1 x :y1 y :x2 (+ x 100) :y2 (+ y 100)} props {:x1 x :y1 y :x2 (+ x 100) :y2 (+ y 100)}
shape (sh/-initialize shape props)] shape (sh/-initialize shape props)]
(rs/emit! (dw/add-shape shape) (rs/emit! (dw/add-shape shape)

View File

@ -11,6 +11,7 @@
[uxbox.util.math :as mth] [uxbox.util.math :as mth]
[uxbox.ui.workspace.base :as wb] [uxbox.ui.workspace.base :as wb]
[uxbox.ui.mixins :as mx] [uxbox.ui.mixins :as mx]
[uxbox.util.geom.point :as gpt]
[uxbox.util.dom :as dom])) [uxbox.util.dom :as dom]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -18,17 +19,17 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- resolve-position (defn- resolve-position
[own [x y]] [own pt]
(let [overlay (mx/get-ref-dom own "overlay") (let [overlay (mx/get-ref-dom own "overlay")
brect (.getBoundingClientRect overlay)] brect (.getBoundingClientRect overlay)
[(- x (.-left brect)) bpt (gpt/point (.-left brect) (.-top brect))]
(- y (.-top brect))])) (gpt/subtract pt bpt)))
(defn- get-position (defn- get-position
[own event] [own event]
(let [x (.-clientX event) (->> (gpt/point (.-clientX event)
y (.-clientY event)] (.-clientY event))
(resolve-position own [x y]))) (resolve-position own)))
(defn- on-mouse-down (defn- on-mouse-down
[own local event] [own local event]
@ -41,41 +42,77 @@
(dom/stop-propagation event) (dom/stop-propagation event)
(swap! local assoc :active false)) (swap! local assoc :active false))
(defn- overlay-line-render
[own center pt]
(let [distance (-> (gpt/distance pt center)
(mth/precision 4))
angle (-> (gpt/angle pt center)
(mth/precision 4))
{x1 :x y1 :y} center
{x2 :x y2 :y} pt]
(html
[:g
[:line {:x1 x1 :y1 y1
:x2 x2 :y2 y2
:style {:cursor "cell"}
:stroke-width "2"
:stroke "red"}]
[:text
{:transform (str "translate(" (+ x2 15) "," (- y2 10) ")")}
[:tspan {:x "0" :dy="1.2em"}
(str "distance=" distance)]
[:tspan {:x "0" :y "20" :dy="1.2em"}
(str "angle=" angle)]]])))
(defn- overlay-render (defn- overlay-render
[own local] [own local]
(let [[x1 y1 :as p1] (:pos1 @local) (let [p1 (:pos1 @local)
[x2 y2 :as p2] (:pos2 @local) p2 (:pos2 @local)]
distance (mth/distance p1 p2)]
(html (html
[:svg {:on-mouse-down #(on-mouse-down own local %) [:svg {:on-mouse-down #(on-mouse-down own local %)
:on-mouse-up #(on-mouse-up own local %) :on-mouse-up #(on-mouse-up own local %)
:ref "overlay"} :ref "overlay"}
[:rect {:style {:fill "transparent" :stroke "transparent" :cursor "cell"} [:rect {:style {:fill "transparent"
:stroke "transparent"
:cursor "cell"}
:width wb/viewport-width :width wb/viewport-width
:height wb/viewport-height}] :height wb/viewport-height}]
(if (and x1 x2) (if (and p1 p2)
[:g (overlay-line-render own p1 p2))])))
[:line {:x1 x1 :y1 y1 :x2 x2 :y2 y2
:style {:cursor "cell"} (def ^:private ^:static +immanted-zones+
:stroke-width "2" (let [transform #(vector (- % 7) (+ % 7) %)]
:stroke "red"}] (concat
[:text {:x (+ x2 15) :y y2} (mapv transform (range 0 181 10))
[:tspan (str distance)]]])]))) (mapv (comp transform -) (range 0 181 10)))))
(defn- overlay-will-mount (defn- overlay-will-mount
[own local] [own local]
(letfn [(on-value [[[x y :as pos] ctrl?]] (letfn [(align-position [angle pos]
(reduce (fn [pos [a1 a2 v]]
(if (< a1 angle a2)
(reduced (gpt/update-angle pos v))
pos))
pos
+immanted-zones+))
(on-value-aligned [pos2]
(let [center (:pos1 @local)]
(as-> pos2 $
(gpt/subtract $ center)
(align-position (gpt/angle $) $)
(gpt/add $ center)
(swap! local assoc :pos2 $))))
(on-value-simple [pos2]
(swap! local assoc :pos2 pos2))
(on-value [[pos ctrl?]]
(if ctrl? (if ctrl?
(let [[sx sy] (:pos1 @local) (on-value-aligned pos)
dx (mth/abs (- x sx)) (on-value-simple pos)))]
dy (mth/abs (- y sy))]
(cond
(> dx dy) (swap! local assoc :pos2 [x sy])
(> dy dx) (swap! local assoc :pos2 [sx y])
:else (swap! local assoc :pos2 pos)))
(swap! local assoc :pos2 pos)))]
(as-> wb/mouse-absolute-s $ (as-> wb/mouse-absolute-s $
(rx/dedupe $)
(rx/filter #(:active @local) $) (rx/filter #(:active @local) $)
(rx/map #(resolve-position own %) $) (rx/map #(resolve-position own %) $)
(rx/with-latest-from vector wb/mouse-ctrl-s $) (rx/with-latest-from vector wb/mouse-ctrl-s $)

View File

@ -11,6 +11,7 @@
[uxbox.data.workspace :as dw] [uxbox.data.workspace :as dw]
[uxbox.ui.workspace.base :as wb] [uxbox.ui.workspace.base :as wb]
[uxbox.ui.mixins :as mx] [uxbox.ui.mixins :as mx]
[uxbox.util.geom.point :as gpt]
[uxbox.util.dom :as dom])) [uxbox.util.dom :as dom]))
(defonce selrect-pos (atom nil)) (defonce selrect-pos (atom nil))
@ -46,10 +47,10 @@
[data] [data]
(let [start (:start data) (let [start (:start data)
current (:current data ) current (:current data )
start-x (min (first start) (first current)) start-x (min (:x start) (:x current))
start-y (min (second start) (second current)) start-y (min (:y start) (:y current))
current-x (max (first start) (first current)) current-x (max (:x start) (:x current))
current-y (max (second start) (second current)) current-y (max (:y start) (:y current))
width (- current-x start-x) width (- current-x start-x)
height (- current-y start-y)] height (- current-y start-y)]
{:x start-x {:x start-x
@ -58,9 +59,8 @@
:height (- current-y start-y)})) :height (- current-y start-y)}))
(define-once :selrect-subscriptions (define-once :selrect-subscriptions
(letfn [(on-value [[x y :as pos]] (letfn [(on-value [pos]
(let [scroll (or @wb/scroll-top 0) (let [pos (gpt/subtract pos @wb/scroll)]
pos [x (+ y scroll)]]
(if (nil? @selrect-pos) (if (nil? @selrect-pos)
(reset! selrect-pos {:start pos :current pos}) (reset! selrect-pos {:start pos :current pos})
(swap! selrect-pos assoc :current pos)))) (swap! selrect-pos assoc :current pos))))

View File

@ -7,6 +7,7 @@
[uxbox.data.workspace :as dw] [uxbox.data.workspace :as dw]
[uxbox.ui.icons :as i] [uxbox.ui.icons :as i]
[uxbox.ui.mixins :as mx] [uxbox.ui.mixins :as mx]
[uxbox.util.geom.point :as gpt]
[uxbox.util.dom :as dom] [uxbox.util.dom :as dom]
[uxbox.ui.colorpicker :refer (colorpicker)] [uxbox.ui.colorpicker :refer (colorpicker)]
[uxbox.ui.workspace.recent-colors :refer (recent-colors)] [uxbox.ui.workspace.recent-colors :refer (recent-colors)]
@ -53,19 +54,12 @@
;; Helpers ;; Helpers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- viewportcoord->clientcoord
[pageid viewport-x viewport-y]
(let [[offset-x offset-y] (get @wb/bounding-rect pageid)
new-x (+ viewport-x offset-x)
new-y (+ viewport-y offset-y)]
[new-x new-y]))
(defn- get-position (defn- get-position
[{:keys [page] :as shape}] [{:keys [page] :as shape}]
(let [{:keys [x y width]} (sh/-outer-rect shape) (let [{:keys [x y width]} (sh/-outer-rect shape)
vx (+ x width 50) bpt (get @wb/bounding-rect page)
vy (- y 50)] vpt (gpt/point (+ x width 50) (- y 50))]
(viewportcoord->clientcoord page vx vy))) (gpt/add vpt bpt)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Implementation ;; Implementation
@ -496,16 +490,17 @@
(defn element-opts-render (defn element-opts-render
[own shape] [own shape]
(let [local (:rum/local own) (let [local (:rum/local own)
shape (rum/react shape)
[popup-x popup-y] (get-position shape)
scroll (or (rum/react wb/scroll-top) 0)
zoom 1 zoom 1
shape (rum/react shape)
scroll (rum/react wb/scroll)
pos (-> (get-position shape)
(gpt/subtract scroll)) ;; and multiply by zoom in future
menus (get +menus-map+ (:type shape)) menus (get +menus-map+ (:type shape))
active-menu (:menu @local (first menus))] active-menu (:menu @local (first menus))]
(when (seq menus) (when (seq menus)
(html (html
[:div#element-options.element-options [:div#element-options.element-options
{:style {:left (* popup-x zoom) :top (- (* popup-y zoom) scroll)}} {:style {:left (:x pos) :top (:y pos)}}
[:ul.element-icons [:ul.element-icons
(for [menu-id (get +menus-map+ (:type shape)) (for [menu-id (get +menus-map+ (:type shape))
:let [menu (get +menus-by-id+ menu-id) :let [menu (get +menus-by-id+ menu-id)