diff --git a/src/uxbox/data/worker.cljs b/src/uxbox/data/worker.cljs new file mode 100644 index 000000000..6a549362c --- /dev/null +++ b/src/uxbox/data/worker.cljs @@ -0,0 +1,32 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) 2016 Andrey Antukh + +(ns uxbox.data.worker + "Worker related api and initialization events." + (:require [beicon.core :as rx] + [uxbox.rstore :as rs] + [uxbox.constants :as c] + [uxbox.util.workers :as uw])) + +(defonce worker (uw/init "/js/worker.js")) + +;; --- Worker Initialization + +(defrecord InitializeWorker [id] + rs/EffectEvent + (-apply-effect [_ state] + (let [page (get-in state [:pages-by-id id]) + opts (:options page) + message {:cmd :grid/init + :width c/viewport-width + :height c/viewport-height + :x-axis (:grid/x-axis opts c/grid-x-axis) + :y-axis (:grid/y-axis opts c/grid-y-axis)}] + (uw/send! worker message)))) + +(defn initialize + [id] + (InitializeWorker. id)) diff --git a/src/uxbox/data/workspace.cljs b/src/uxbox/data/workspace.cljs index af6af7891..445013b05 100644 --- a/src/uxbox/data/workspace.cljs +++ b/src/uxbox/data/workspace.cljs @@ -14,31 +14,41 @@ [uxbox.schema :as sc] [uxbox.data.pages :as udp] [uxbox.data.shapes :as uds] - ;; [uxbox.data.worker :as wrk] + [uxbox.data.worker :as wrk] [uxbox.util.datetime :as dt] [uxbox.util.geom.point :as gpt])) ;; --- Workspace Initialization +(defrecord InitializeWorkspace [project page] + rs/UpdateEvent + (-apply-update [_ state] + (if (:workspace state) + (update state :workspace merge + {:project project + :page page + :selected #{} + :drawing nil}) + (assoc state :workspace + {:project project + :zoom 1 + :page page + :flags #{:layers :element-options} + :selected #{} + :drawing nil}))) + + rs/WatchEvent + (-apply-watch [_ state s] + (if (get-in state [:pages-by-id page]) + (rx/of (wrk/initialize page)) + (->> (rx/filter udp/pages-fetched? s) + (rx/take 1) + (rx/map #(wrk/initialize page)))))) + (defn initialize "Initialize the workspace state." [project page] - (reify - rs/UpdateEvent - (-apply-update [_ state] - (if (:workspace state) - (update state :workspace merge - {:project project - :page page - :selected #{} - :drawing nil}) - (assoc state :workspace - {:project project - :zoom 1 - :page page - :flags #{:layers :element-options} - :selected #{} - :drawing nil}))))) + (InitializeWorkspace. project page)) (defn toggle-flag "Toggle the enabled flag of the specified tool." diff --git a/src/uxbox/ui/workspace/align.cljs b/src/uxbox/ui/workspace/align.cljs new file mode 100644 index 000000000..d57eca2ce --- /dev/null +++ b/src/uxbox/ui/workspace/align.cljs @@ -0,0 +1,37 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) 2016 Andrey Antukh + +(ns uxbox.ui.workspace.align + "Shape alignmen impl." + (:require [beicon.core :as rx] + [lentes.core :as l] + [uxbox.state :as st] + [uxbox.shapes :as sh] + [uxbox.data.worker :refer (worker)] + [uxbox.ui.workspace.base :as wb] + [uxbox.util.geom.point :as gpt] + [uxbox.util.workers :as uw])) + +(defn- move + [shape p1] + (let [dx (- (:x2 shape) (:x1 shape)) + dy (- (:y2 shape) (:y1 shape)) + p2 (gpt/add p1 [dx dy])] + (assoc shape + :x1 (:x p1) + :y1 (:y p1) + :x2 (:x p2) + :y2 (:y p2)))) + +(defn translate + [{:keys [x1 y1] :as shape}] + (let [message {:cmd :grid/align + :point (gpt/point x1 y1)}] + (->> (uw/ask! worker message) + (rx/map (fn [{:keys [point]}] + (if point + (move shape point) + shape)))))) diff --git a/src/uxbox/ui/workspace/movement.cljs b/src/uxbox/ui/workspace/movement.cljs index b80d30485..364994cad 100644 --- a/src/uxbox/ui/workspace/movement.cljs +++ b/src/uxbox/ui/workspace/movement.cljs @@ -9,6 +9,7 @@ "Shape movement in workspace logic." (:require [beicon.core :as rx] [lentes.core :as l] + [uxbox.constants :as c] [uxbox.rstore :as rs] [uxbox.state :as st] [uxbox.shapes :as sh] @@ -23,16 +24,24 @@ ;; --- Lenses +(declare translate-to-viewport) + (defn- resolve-selected [state] (let [selected (get-in state [:workspace :selected]) - xf (map #(get-in state [:shapes-by-id %]))] + xf (comp + (map #(get-in state [:shapes-by-id %])) + (map translate-to-viewport))] (into #{} xf selected))) (def ^:const ^:private selected-shapes-l (-> (l/getter resolve-selected) (l/focus-atom st/state))) +(def ^:const ^:privae page-options-l + (-> (l/key :options) + (l/focus-atom wb/page-l))) + ;; --- Public Api (defn watch-move-actions @@ -43,9 +52,40 @@ ;; --- Implementation +(def coords + (gpt/point c/canvas-start-x + c/canvas-start-y)) + +(defn- translate-to-viewport + [shape] + (let [dx (- (:x2 shape) (:x1 shape)) + dy (- (:y2 shape) (:y1 shape)) + p1 (gpt/point (:x1 shape) (:y1 shape)) + p2 (gpt/add p1 coords) + p3 (gpt/add p2 [dx dy])] + (assoc shape + :x1 (:x p2) + :y1 (:y p2) + :x2 (:x p3) + :y2 (:y p3)))) + +(defn- translate-to-canvas + [shape] + (let [dx (- (:x2 shape) (:x1 shape)) + dy (- (:y2 shape) (:y1 shape)) + p1 (gpt/point (:x1 shape) (:y1 shape)) + p2 (gpt/subtract p1 coords) + p3 (gpt/add p2 [dx dy])] + (assoc shape + :x1 (:x p2) + :y1 (:y p2) + :x2 (:x p3) + :y2 (:y p3)))) + (defn- initialize [] (let [shapes @selected-shapes-l + options @page-options-l stoper (->> uuc/actions-s (rx/map :type) (rx/filter empty?) @@ -53,7 +93,17 @@ (as-> wb/mouse-delta-s $ (rx/take-until stoper $) (rx/scan (fn [acc delta] - (mapv #(sh/move % delta) acc)) shapes $) + (let [xf (map #(sh/move % delta))] + (into [] xf acc))) shapes $) + (rx/mapcat (fn [items] + (if (:grid/align options) + (->> (apply rx/of items) + (rx/mapcat align/translate) + (rx/reduce conj [])) + (rx/of items))) $) + (rx/map (fn [items] + (mapv translate-to-canvas items)) $) + (rx/subscribe $ handle-movement)))) (defn- handle-movement @@ -61,3 +111,4 @@ (doseq [shape delta] (rs/emit! (uds/update-shape shape)))) + diff --git a/src/uxbox/util/workers.cljs b/src/uxbox/util/workers.cljs new file mode 100644 index 000000000..e96b9a1dd --- /dev/null +++ b/src/uxbox/util/workers.cljs @@ -0,0 +1,62 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) 2016 Andrey Antukh + +(ns uxbox.util.workers + "A lightweight layer on top of webworkers api." + (:require [beicon.core :as rx] + [uxbox.util.transit :as t])) + +;; --- Implementation + +(defprotocol IWorker + (-ask [_ msg] "Send and receive message as rx stream.") + (-send [_ msg] "Send message and forget.")) + +(deftype WebWorker [stream wrk] + IWorker + (-ask [this message] + (let [sender (random-uuid) + data (assoc message :sender sender) + data (t/encode data)] + (.postMessage wrk data) + (->> stream + (rx/filter #(= (:reply-to %) sender)) + (rx/take 1)))) + + (-send [this message] + (let [sender (random-uuid) + data (assoc message :sender sender) + data (t/encode data)] + (.postMessage wrk data) + (->> stream + (rx/filter #(= (:reply-to %) sender)))))) + +;; --- Public Api + +(defn init + "Return a initialized webworker instance." + [path] + (let [wrk (js/Worker. path) + bus (rx/bus)] + (.addEventListener wrk "message" + (fn [event] + (let [data (.-data event) + data (t/decode data)] + (rx/push! bus data)))) + (.addEventListener wrk "error" + (fn [event] + (rx/error! bus event))) + + (WebWorker. (rx/map identity bus) wrk))) + +(defn ask! + [wrk message] + (-ask wrk message)) + +(defn send! + [wrk message] + (-send wrk message)) + diff --git a/src/uxbox/worker/align.cljs b/src/uxbox/worker/align.cljs new file mode 100644 index 000000000..704fb908c --- /dev/null +++ b/src/uxbox/worker/align.cljs @@ -0,0 +1,34 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) 2016 Andrey Antukh + +(ns uxbox.worker.align + "Workspace aligment indexes worker." + (:require [beicon.core :as rx] + [kdtree :as kd] + [uxbox.worker.core :as wrk] + [uxbox.util.geom.point :as gpt])) + +(defonce state (volatile! nil)) + +(defmethod wrk/handler :grid/init + [{:keys [width height x-axis y-axis] :as opts}] + (println ":grid/init" opts) + (let [points (into-array + (for [x (range 0 width (or x-axis 10)) + y (range 0 height (or y-axis 10))] + #js [x y])) + tree (kd/create2d points)] + (vreset! state tree))) + +(defmethod wrk/handler :grid/align + [{:keys [sender point] :as message}] + (println "request" point) + (let [point #js [(:x point) (:y point)] + results (js->clj (.nearest @state point 1)) + [[x y] d] (first results) + result (gpt/point x y)] + (println "result:" result) + (wrk/reply! sender {:point (gpt/point x y)})))