2020-04-30 11:37:36 +02:00
|
|
|
;; 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/.
|
|
|
|
;;
|
|
|
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
|
|
|
;; defined by the Mozilla Public License, v. 2.0.
|
|
|
|
;;
|
|
|
|
;; Copyright (c) 2020 UXBOX Labs SL
|
|
|
|
|
2020-05-11 09:46:57 +02:00
|
|
|
(ns uxbox.main.snap
|
2020-04-30 11:37:36 +02:00
|
|
|
(:require
|
2020-05-08 15:53:30 +02:00
|
|
|
[beicon.core :as rx]
|
2020-04-30 11:37:36 +02:00
|
|
|
[uxbox.common.uuid :refer [zero]]
|
2020-05-11 09:46:57 +02:00
|
|
|
[uxbox.util.math :as mth]
|
2020-05-07 11:26:50 +02:00
|
|
|
[uxbox.util.geom.point :as gpt]
|
2020-05-11 09:46:57 +02:00
|
|
|
[uxbox.main.worker :as uw]
|
|
|
|
[uxbox.util.geom.snap-points :as sp]))
|
2020-04-30 11:37:36 +02:00
|
|
|
|
2020-05-11 09:46:57 +02:00
|
|
|
(def ^:private snap-accuracy 5)
|
2020-05-08 15:53:30 +02:00
|
|
|
|
2020-05-11 09:46:57 +02:00
|
|
|
(defn- remove-from-snap-points [ids-to-remove]
|
2020-05-08 15:53:30 +02:00
|
|
|
(fn [query-result]
|
|
|
|
(->> query-result
|
|
|
|
(map (fn [[value data]] [value (remove (comp ids-to-remove second) data)]))
|
|
|
|
(filter (fn [[_ data]] (not (empty? data)))))))
|
|
|
|
|
2020-05-11 09:46:57 +02:00
|
|
|
(defn- flatten-to-points
|
|
|
|
[query-result]
|
|
|
|
(mapcat (fn [[v data]] (map (fn [[point _]] point) data)) query-result))
|
|
|
|
|
2020-05-08 15:53:30 +02:00
|
|
|
(defn- calculate-distance [query-result point coord]
|
|
|
|
(->> query-result
|
|
|
|
(map (fn [[value data]] [(mth/abs (- value (coord point))) [(coord point) value]]))))
|
|
|
|
|
|
|
|
(defn- get-min-distance-snap [points coord]
|
|
|
|
(fn [query-result]
|
|
|
|
(->> points
|
|
|
|
(mapcat #(calculate-distance query-result % coord))
|
|
|
|
(apply min-key first)
|
|
|
|
second)))
|
2020-04-30 11:37:36 +02:00
|
|
|
|
2020-05-11 09:46:57 +02:00
|
|
|
(defn- snap-frame-id [shapes]
|
2020-04-30 11:37:36 +02:00
|
|
|
(let [frames (into #{} (map :frame-id shapes))]
|
|
|
|
(cond
|
|
|
|
;; Only shapes from one frame. The common is the only one
|
|
|
|
(= 0 (count frames)) (first frames)
|
|
|
|
|
|
|
|
;; Frames doesn't contain zero. So we take the first frame
|
|
|
|
(not (frames zero)) (-> shapes first :frame-id)
|
2020-05-05 13:32:58 +02:00
|
|
|
|
2020-04-30 11:37:36 +02:00
|
|
|
;; Otherwise the root frame is the common
|
|
|
|
:else zero)))
|
|
|
|
|
2020-05-08 15:53:30 +02:00
|
|
|
(defn get-snap-points [page-id frame-id filter-shapes point coord]
|
|
|
|
(let [value (coord point)]
|
|
|
|
(->> (uw/ask! {:cmd :snaps/range-query
|
|
|
|
:page-id page-id
|
|
|
|
:frame-id frame-id
|
|
|
|
:coord coord
|
|
|
|
:ranges [[(- value 1) (+ value 1)]]})
|
|
|
|
(rx/first)
|
|
|
|
(rx/map (remove-from-snap-points filter-shapes))
|
|
|
|
(rx/map flatten-to-points))))
|
|
|
|
|
|
|
|
(defn- search-snap
|
|
|
|
[page-id frame-id points coord filter-shapes]
|
|
|
|
(let [ranges (->> points
|
|
|
|
(map coord)
|
|
|
|
(mapv #(vector (- % snap-accuracy)
|
|
|
|
(+ % snap-accuracy))))]
|
|
|
|
(->> (uw/ask! {:cmd :snaps/range-query
|
|
|
|
:page-id page-id
|
|
|
|
:frame-id frame-id
|
|
|
|
:coord coord
|
|
|
|
:ranges ranges})
|
|
|
|
(rx/first)
|
|
|
|
(rx/map (remove-from-snap-points filter-shapes))
|
|
|
|
(rx/map (get-min-distance-snap points coord)))))
|
2020-04-30 11:37:36 +02:00
|
|
|
|
2020-05-08 15:53:30 +02:00
|
|
|
(defn- closest-snap
|
|
|
|
[page-id frame-id points filter-shapes]
|
|
|
|
(let [snap-x (search-snap page-id frame-id points :x filter-shapes)
|
|
|
|
snap-y (search-snap page-id frame-id points :y filter-shapes)
|
|
|
|
snap-as-vector (fn [[from-x to-x] [from-y to-y]]
|
|
|
|
(let [from (gpt/point (or from-x 0) (or from-y 0))
|
|
|
|
to (gpt/point (or to-x 0) (or to-y 0))]
|
|
|
|
(gpt/to-vec from to)))]
|
|
|
|
;; snap-x is the second parameter because is the "source" to combine
|
|
|
|
(rx/combine-latest snap-as-vector snap-y snap-x)))
|
2020-04-30 11:37:36 +02:00
|
|
|
|
2020-05-05 10:33:14 +02:00
|
|
|
(defn closest-snap-point
|
2020-05-11 09:46:57 +02:00
|
|
|
[page-id shapes layout point]
|
|
|
|
(if (layout :dynamic-alignment)
|
|
|
|
(let [frame-id (snap-frame-id shapes)
|
|
|
|
filter-shapes (into #{} (map :id shapes))]
|
|
|
|
(->> (closest-snap page-id frame-id [point] filter-shapes)
|
|
|
|
(rx/map #(gpt/add point %))))
|
|
|
|
(rx/of point)))
|
2020-04-30 11:37:36 +02:00
|
|
|
|
2020-05-05 10:33:14 +02:00
|
|
|
(defn closest-snap-move
|
2020-05-11 09:46:57 +02:00
|
|
|
[page-id shapes layout movev]
|
|
|
|
(if (layout :dynamic-alignment)
|
|
|
|
(let [frame-id (snap-frame-id shapes)
|
|
|
|
filter-shapes (into #{} (map :id shapes))
|
|
|
|
shapes-points (->> shapes
|
|
|
|
;; Unroll all the possible snap-points
|
|
|
|
(mapcat (partial sp/shape-snap-points))
|
|
|
|
|
|
|
|
;; Move the points in the translation vector
|
|
|
|
(map #(gpt/add % movev)))]
|
|
|
|
(->> (closest-snap page-id frame-id shapes-points filter-shapes)
|
|
|
|
(rx/map #(gpt/add movev %))))
|
|
|
|
(rx/of movev)))
|