mirror of
https://github.com/penpot/penpot.git
synced 2025-01-09 00:10:11 -05:00
Add initial approach for grid alignment.
A little bit buggy because the main algorithm does not works well.
This commit is contained in:
parent
051163c3ae
commit
935bafd512
6 changed files with 245 additions and 19 deletions
32
src/uxbox/data/worker.cljs
Normal file
32
src/uxbox/data/worker.cljs
Normal file
|
@ -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 <niwi@niwi.nz>
|
||||
|
||||
(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))
|
|
@ -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."
|
||||
|
|
37
src/uxbox/ui/workspace/align.cljs
Normal file
37
src/uxbox/ui/workspace/align.cljs
Normal file
|
@ -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 <niwi@niwi.nz>
|
||||
|
||||
(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))))))
|
|
@ -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))))
|
||||
|
||||
|
||||
|
|
62
src/uxbox/util/workers.cljs
Normal file
62
src/uxbox/util/workers.cljs
Normal file
|
@ -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 <niwi@niwi.nz>
|
||||
|
||||
(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))
|
||||
|
34
src/uxbox/worker/align.cljs
Normal file
34
src/uxbox/worker/align.cljs
Normal file
|
@ -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 <niwi@niwi.nz>
|
||||
|
||||
(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)})))
|
Loading…
Reference in a new issue