diff --git a/common/uxbox/common/data.cljc b/common/uxbox/common/data.cljc index d49dc238e..85694129a 100644 --- a/common/uxbox/common/data.cljc +++ b/common/uxbox/common/data.cljc @@ -110,6 +110,33 @@ [mfn coll] (into {} (map (fn [[key val]] [key (mfn key val)]) coll))) +(defn map-perm + "Maps a function to each pair of values that can be combined inside the + function without repetition. + Example: + (map-perm vector [1 2 3 4]) => [[1 2] [1 3] [1 4] [2 3] [2 4] [3 4]]" + [mfn coll] + (if (empty? coll) + [] + (core/concat + (map (partial mfn (first coll)) (rest coll)) + (map-perm mfn (rest coll))))) + +(defn join + "Returns a new collection with the cartesian product of both collections. + For example: + (join [1 2 3] [:a :b]) => ([1 :a] [1 :b] [2 :a] [2 :b] [3 :a] [3 :b]) + You can pass a function to merge the items. By default is `vector`: + (join [1 2 3] [1 10 100] *) => (1 10 100 2 20 200 3 30 300)" + ([col1 col2] (join col1 col2 vector '())) + ([col1 col2 join-fn] (join col1 col2 join-fn '())) + ([col1 col2 join-fn acc] + (cond + (empty? col1) acc + (empty? col2) acc + :else (recur (rest col1) col2 join-fn + (core/concat acc (map (partial join-fn (first col1)) col2)))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Data Parsing / Conversion ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/common/uxbox/common/geom/point.cljc b/common/uxbox/common/geom/point.cljc index fea614018..95277b9a8 100644 --- a/common/uxbox/common/geom/point.cljc +++ b/common/uxbox/common/geom/point.cljc @@ -73,12 +73,22 @@ (defn min - [{x1 :x y1 :y :as p1} {x2 :x y2 :y :as p2}] - (Point. (c/min x1 x2) (c/min y1 y2))) + ([] (min nil nil)) + ([p1] (min p1 nil)) + ([{x1 :x y1 :y :as p1} {x2 :x y2 :y :as p2}] + (cond + (nil? p1) p2 + (nil? p2) p1 + :else (Point. (c/min x1 x2) (c/min y1 y2))))) (defn max - [{x1 :x y1 :y :as p1} {x2 :x y2 :y :as p2}] - (Point. (c/max x1 x2) (c/max y1 y2))) + ([] (max nil nil)) + ([p1] (max p1 nil)) + ([{x1 :x y1 :y :as p1} {x2 :x y2 :y :as p2}] + (cond + (nil? p1) p2 + (nil? p2) p1 + :else (Point. (c/max x1 x2) (c/max y1 y2))))) (defn inverse [{:keys [x y] :as p}] diff --git a/common/uxbox/common/geom/shapes.cljc b/common/uxbox/common/geom/shapes.cljc index a29f46082..b69700935 100644 --- a/common/uxbox/common/geom/shapes.cljc +++ b/common/uxbox/common/geom/shapes.cljc @@ -48,7 +48,11 @@ (update :y inc-y))))] (-> shape (update :x inc-x) + (update :x1 inc-x) + (update :x2 inc-x) (update :y inc-y) + (update :y1 inc-y) + (update :y2 inc-y) (update-in [:selrect :x] inc-x) (update-in [:selrect :x1] inc-x) (update-in [:selrect :x2] inc-x) @@ -548,7 +552,6 @@ :type :rect}] (overlaps? shape selrect))) - (defn calculate-rec-path-skew-angle [path-shape] (let [p1 (get-in path-shape [:segments 2]) @@ -587,6 +590,63 @@ rot-sign (if (> (* (:y v1) (:x v2)) (* (:x v1) (:y v2))) -1 1)] (* rot-sign rot-angle))) +(defn pad-selrec + ([selrect] (pad-selrec selrect 1)) + ([selrec size] + (let [inc #(+ % size) + dec #(- % size)] + (-> selrec + (update :x dec) + (update :y dec) + (update :x1 dec) + (update :y1 dec) + (update :x2 inc) + (update :y2 inc) + (update :width (comp inc inc)) + (update :height (comp inc inc)))))) + +(defn selrect->areas [bounds selrect] + (let [make-selrect + (fn [x1 y1 x2 y2] + {:x1 x1 :y1 y1 :x2 x2 :y2 y2 :x x1 :y y1 + :width (- x2 x1) :height (- y2 y1) :type :rect}) + {frame-x1 :x1 frame-x2 :x2 frame-y1 :y1 frame-y2 :y2 + frame-width :width frame-height :height} bounds + {sr-x1 :x1 sr-x2 :x2 sr-y1 :y1 sr-y2 :y2 + sr-width :width sr-height :height} selrect] + {:left (make-selrect frame-x1 sr-y1 sr-x1 sr-y2) + :top (make-selrect sr-x1 frame-y1 sr-x2 sr-y1) + :right (make-selrect sr-x2 sr-y1 frame-x2 sr-y2) + :bottom (make-selrect sr-x1 sr-y2 sr-x2 frame-y2)})) + +(defn distance-selrect [selrect other] + (let [{:keys [x1 y1]} other + {:keys [x2 y2]} selrect] + (gpt/point (- x1 x2) (- y1 y2)))) + +(defn distance-shapes [shape other] + (distance-selrect (:selrect shape) (:selrect other))) + +(defn overlap-coord? + "Checks if two shapes overlap in one axis" + [coord shape other] + (let [[s1c1 s1c2 s2c1 s2c2] + ;; If checking if overlaps in x-axis we need to check the y + ;; coordinates, and the other way around + (if (= coord :x) + [(get-in shape [:selrect :y1]) + (get-in shape [:selrect :y2]) + (get-in other [:selrect :y1]) + (get-in other [:selrect :y2])] + [(get-in shape [:selrect :x1]) + (get-in shape [:selrect :x2]) + (get-in other [:selrect :x1]) + (get-in other [:selrect :x2])])] + (or (and (>= s2c1 s1c1) (<= s2c1 s1c2)) + (and (>= s2c2 s1c1) (<= s2c2 s1c2)) + (and (>= s1c1 s2c1) (<= s1c1 s2c2)) + (and (>= s1c2 s2c1) (<= s1c2 s2c2))))) + (defn transform-shape-point "Transform a point around the shape center" [point shape transform] diff --git a/frontend/src/uxbox/main/data/workspace/transforms.cljs b/frontend/src/uxbox/main/data/workspace/transforms.cljs index 107bdb5a5..4141c9026 100644 --- a/frontend/src/uxbox/main/data/workspace/transforms.cljs +++ b/frontend/src/uxbox/main/data/workspace/transforms.cljs @@ -219,6 +219,7 @@ ptk/WatchEvent (watch [_ state stream] (let [page-id (get state :current-page-id) + objects (get-in state [:workspace-data page-id :objects]) shapes (mapv #(get-in state [:workspace-data page-id :objects %]) ids) stopper (rx/filter ms/mouse-up? stream) layout (get state :workspace-layout)] @@ -226,7 +227,7 @@ (->> ms/mouse-position (rx/take-until stopper) (rx/map #(gpt/to-vec from-position %)) - (rx/switch-map #(snap/closest-snap-move page-id shapes layout %)) + (rx/switch-map #(snap/closest-snap-move page-id shapes objects layout %)) (rx/map gmt/translate-matrix) (rx/map #(set-modifiers ids {:displacement %}))) diff --git a/frontend/src/uxbox/main/snap.cljs b/frontend/src/uxbox/main/snap.cljs index 45b25ed14..55287be3f 100644 --- a/frontend/src/uxbox/main/snap.cljs +++ b/frontend/src/uxbox/main/snap.cljs @@ -9,14 +9,18 @@ (ns uxbox.main.snap (:require + [clojure.set :as set] [beicon.core :as rx] [uxbox.common.uuid :refer [zero]] [uxbox.common.math :as mth] + [uxbox.common.data :as d] [uxbox.common.geom.point :as gpt] + [uxbox.common.geom.shapes :as gsh] [uxbox.main.worker :as uw] [uxbox.util.geom.snap-points :as sp])) (def ^:private snap-accuracy 5) +(def ^:private snap-distance-accuracy 10) (defn- remove-from-snap-points [remove-id?] (fn [query-result] @@ -77,16 +81,91 @@ (rx/map (remove-from-snap-points filter-shapes)) (rx/map (get-min-distance-snap points coord))))) +(defn snap->vector [[from-x to-x] [from-y to-y]] + (when (or 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)))) + (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-y (search-snap page-id frame-id points :y filter-shapes)] ;; snap-x is the second parameter because is the "source" to combine - (rx/combine-latest snap-as-vector snap-y snap-x))) + (rx/combine-latest snap->vector snap-y snap-x))) + +(defn search-snap-distance [selrect coord shapes-lt shapes-gt] + (let [dist (fn [[sh1 sh2]] (-> sh1 (gsh/distance-shapes sh2) coord)) + dist-lt (fn [other] (-> (:selrect other) (gsh/distance-selrect selrect) coord)) + dist-gt (fn [other] (-> selrect (gsh/distance-selrect (:selrect other)) coord)) + + ;; Calculates the distance between all the shapes given as argument + inner-distance (fn [shapes] + (->> shapes + (sort-by coord) + (d/map-perm vector) + (filter (fn [[sh1 sh2]] (gsh/overlap-coord? coord sh1 sh2))) + (map dist) + (filter #(> % 0)))) + + ;; Calculates the snap distance when in the middle of two shapes + between-snap (fn [[sh-lt sh-gt]] + ;; To calculate the middle snap. + ;; Given x, the distance to a left shape and y to a right shape + ;; x - v = y + v => v = (x - y)/2 + ;; v will be the vector that we need to move the shape so it "snaps" + ;; in the middle + (/ (- (dist-gt sh-gt) + (dist-lt sh-lt)) 2)) + ] + (->> shapes-lt + (rx/combine-latest vector shapes-gt) + (rx/map (fn [[shapes-lt shapes-gt]] + (let [;; Distance between the elements in an area, these are the snap + ;; candidates to either side + lt-cand (inner-distance shapes-lt) + gt-cand (inner-distance shapes-gt) + + ;; Distance between the elements to either side and the current shape + ;; this is the distance that will "snap" + lt-dist (map dist-lt shapes-lt) + gt-dist (map dist-gt shapes-gt) + + ;; Calculate the snaps, we need to reverse depending on area + lt-snap (d/join lt-cand lt-dist -) + gt-snap (d/join gt-dist gt-cand -) + + ;; Calculate snap-between + between-snap (->> (d/join shapes-lt shapes-gt) + (map between-snap)) + + ;; Search the minimum snap + min-snap (->> (concat lt-snap gt-snap between-snap) + (filter #(<= (mth/abs %) snap-distance-accuracy)) + (reduce min ##Inf))] + + (if (mth/finite? min-snap) [0 min-snap] nil)) + + ))))) + +(defn select-shapes-area [page-id shapes objects area-selrect] + (->> (uw/ask! {:cmd :selection/query + :page-id page-id + :rect area-selrect}) + (rx/map #(set/difference % (into #{} (map :id shapes)))) + (rx/map (fn [ids] (map #(get objects %) ids))))) + +(defn closest-distance-snap [page-id shapes objects movev] + (->> (rx/of shapes) + (rx/map #(vector (->> % first :frame-id (get objects)) + (-> % gsh/selection-rect (gsh/move movev)))) + (rx/merge-map + (fn [[frame selrect]] + (let [areas (->> (gsh/selrect->areas (:selrect frame) selrect) + (d/mapm #(select-shapes-area page-id shapes objects %2))) + snap-x (search-snap-distance selrect :x (:left areas) (:right areas)) + snap-y (search-snap-distance selrect :y (:top areas) (:bottom areas))] + (rx/combine-latest snap->vector snap-y snap-x)))))) (defn closest-snap-point [page-id shapes layout point] @@ -98,11 +177,12 @@ (or (filter-shapes id) (not (contains? layout :dynamic-alignment)))))] (->> (closest-snap page-id frame-id [point] filter-shapes) + (rx/map #(or % (gpt/point 0 0))) (rx/map #(gpt/add point %)) (rx/map gpt/round)))) (defn closest-snap-move - [page-id shapes layout movev] + [page-id shapes objects layout movev] (let [frame-id (snap-frame-id shapes) filter-shapes (into #{} (map :id shapes)) filter-shapes (fn [id] (if (= id :layout) @@ -116,6 +196,9 @@ ;; 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/map gpt/round)))) + (->> (rx/merge (closest-snap page-id frame-id shapes-points filter-shapes) + (when (contains? layout :dynamic-alignment) + (closest-distance-snap page-id shapes objects movev))) + (rx/reduce gpt/min) + (rx/map #(or % (gpt/point 0 0))) + (rx/map #(gpt/add movev %))))) diff --git a/frontend/src/uxbox/main/ui/workspace/snap_distances.cljs b/frontend/src/uxbox/main/ui/workspace/snap_distances.cljs new file mode 100644 index 000000000..dfc850e37 --- /dev/null +++ b/frontend/src/uxbox/main/ui/workspace/snap_distances.cljs @@ -0,0 +1,212 @@ +(ns uxbox.main.ui.workspace.snap-distances + (:require + [rumext.alpha :as mf] + [beicon.core :as rx] + [uxbox.common.uuid :as uuid] + [uxbox.main.refs :as refs] + [uxbox.main.snap :as snap] + [uxbox.util.geom.snap-points :as sp] + [uxbox.common.geom.point :as gpt] + + [cuerdas.core :as str] + [uxbox.common.pages :as cp] + [uxbox.common.data :as d] + [uxbox.common.geom.shapes :as gsh] + [uxbox.common.math :as mth] + [uxbox.main.worker :as uw] + [clojure.set :as set])) + +(def ^:private line-color "#D383DA") +(def ^:private segment-gap 2) +(def ^:private segment-gap-side 5) + +(defn selected->cross-selrec [frame selrect coord] + (let [areas (gsh/selrect->areas (:selrect frame) selrect)] + (if (= :x coord) + [(gsh/pad-selrec (:left areas)) + (gsh/pad-selrec (:right areas))] + [(gsh/pad-selrec (:top areas)) + (gsh/pad-selrec (:bottom areas))]))) + +(defn half-point + "Calculates the middle point of the overlap between two selrects in the opposite axis" + [coord sr1 sr2] + (let [c1 (max (get sr1 (if (= :x coord) :y1 :x1)) + (get sr2 (if (= :x coord) :y1 :x1))) + c2 (min (get sr1 (if (= :x coord) :y2 :x2)) + (get sr2 (if (= :x coord) :y2 :x2))) + half-point (+ c1 (/ (- c2 c1) 2))] + half-point)) + + +(mf/defc shape-distance-segment + "Displays a segment between two selrects with the distance between them" + [{:keys [sr1 sr2 coord zoom]}] + (let [from-c (min (get sr1 (if (= :x coord) :x2 :y2)) + (get sr2 (if (= :x coord) :x2 :y2))) + to-c (max (get sr1 (if (= :x coord) :x1 :y1)) + (get sr2 (if (= :x coord) :x1 :y1))) + distance (mth/round (- to-c from-c)) + half-point (half-point coord sr1 sr2)] + + [:g.distance-segment + (let [point [(+ from-c (/ distance 2)) + (if (= coord :x) + (- half-point (/ 3 zoom)) + (+ half-point (/ 5 zoom)))] + [x y] (if (= :x coord) point (reverse point))] + [:text {:x x + :y y + :font-size (/ 12 zoom) + :fill line-color + :text-anchor (if (= coord :x) "middle" "left")} + (mth/round distance)]) + + (let [p1 [(+ from-c (/ segment-gap zoom)) (+ half-point (/ segment-gap-side zoom))] + p2 [(+ from-c (/ segment-gap zoom)) (- half-point (/ segment-gap-side zoom))] + [x1 y1] (if (= :x coord) p1 (reverse p1)) + [x2 y2] (if (= :x coord) p2 (reverse p2))] + [:line {:x1 x1 :y1 y1 + :x2 x2 :y2 y2 + :style {:stroke line-color :stroke-width (str (/ 1 zoom))}}]) + (let [p1 [(- to-c (/ segment-gap zoom)) (+ half-point (/ segment-gap-side zoom))] + p2 [(- to-c (/ segment-gap zoom)) (- half-point (/ segment-gap-side zoom))] + [x1 y1] (if (= :x coord) p1 (reverse p1)) + [x2 y2] (if (= :x coord) p2 (reverse p2))] + [:line {:x1 x1 :y1 y1 + :x2 x2 :y2 y2 + :style {:stroke line-color :stroke-width (str (/ 1 zoom))}}]) + (let [p1 [(+ from-c (/ segment-gap zoom)) half-point] + p2 [(- to-c (/ segment-gap zoom)) half-point] + [x1 y1] (if (= :x coord) p1 (reverse p1)) + [x2 y2] (if (= :x coord) p2 (reverse p2))] + [:line {:x1 x1 :y1 y1 + :x2 x2 :y2 y2 + :style {:stroke line-color :stroke-width (str (/ 1 zoom))}}])])) + +(mf/defc shape-distance [{:keys [frame selrect page-id zoom coord selected]}] + (let [subject (mf/use-memo #(rx/subject)) + to-measure (mf/use-state []) + + pair->distance+pair + (fn [[sh1 sh2]] + [(-> (gsh/distance-shapes sh1 sh2) coord mth/round) [sh1 sh2]]) + + contains-selected? + (fn [selected pairs] + (let [has-selected? + (fn [[_ [sh1 sh2]]] + (or (selected (:id sh1)) + (selected (:id sh2))))] + (some has-selected? pairs))) + + query-worker + (fn [[selrect selected frame]] + (let [lt-side (if (= coord :x) :left :top) + gt-side (if (= coord :x) :right :bottom) + areas (gsh/selrect->areas (:selrect frame) selrect) + query-side (fn [side] + (->> (uw/ask! {:cmd :selection/query + :page-id page-id + :rect (gsh/pad-selrec (areas side))}) + (rx/map #(set/difference % selected)) + (rx/map #(->> % (map (partial get @refs/workspace-objects))))))] + + (->> (query-side lt-side) + (rx/combine-latest vector (query-side gt-side))))) + + distance-to-selrect + (fn [shape] + (let [sr (:selrect shape)] + (-> (if (<= (coord sr) (coord selrect)) + (gsh/distance-selrect sr selrect) + (gsh/distance-selrect selrect sr)) + coord + mth/round))) + + get-shapes-match + (fn [pred? shapes] + (->> shapes + (sort-by coord) + (d/map-perm vector) + (filter (fn [[sh1 sh2]] (gsh/overlap-coord? coord sh1 sh2))) + (map pair->distance+pair) + (filter (comp pred? first)))) + + ;; Left/Top shapes and right/bottom shapes (depends on `coord` parameter + [lt-shapes gt-shapes] @to-measure + + ;; Gets the distance to the current selection + lt-distances (->> lt-shapes (map distance-to-selrect) (filter pos?) (into #{})) + gt-distances (->> gt-shapes (map distance-to-selrect) (filter pos?) (into #{})) + + ;; We'll show the distances that match a distance from the selrect + show-candidate? (set/union lt-distances gt-distances) + + ;; Checks the distances between elements for distances that match the set of distances + distance-coincidences (concat (get-shapes-match show-candidate? lt-shapes) + (get-shapes-match show-candidate? gt-shapes)) + + ;; Show the distances that either match one of the distances from the selrect + ;; or are from the selrect and go to a shape on the left and to the right + show-distance? (into #{} (concat + (map first distance-coincidences) + (set/intersection lt-distances gt-distances))) + + ;; These are the segments whose distance will be displayed + + ;; First segments from segments different that the selectio + other-shapes-segments (->> distance-coincidences + (map second) ;; Retrieves list of [shape,shape] tuples + (map #(mapv :selrect %))) ;; Changes [shape,shape] to [selrec,selrec] + + ;; Segments from the selection to other + selection-segments (->> (concat lt-shapes gt-shapes) + (filter #(show-distance? (distance-to-selrect %))) + (map #(vector selrect (:selrect %)))) + + segments-to-display (concat other-shapes-segments selection-segments)] + + (mf/use-effect + (fn [] + (let [sub (->> subject + (rx/switch-map query-worker) + (rx/subs #(reset! to-measure %)))] + ;; On unmount dispose + #(rx/dispose! sub)))) + + (mf/use-effect (mf/deps selrect selected) #(rx/push! subject [selrect selected frame])) + + (for [[sr1 sr2] segments-to-display] + [:& shape-distance-segment {:key (str/join "-" [(:x sr1) (:y sr1) (:x sr2) (:y sr2)]) + :sr1 sr1 + :sr2 sr2 + :coord coord + :zoom zoom}]))) + +(mf/defc snap-distances [{:keys [layout]}] + (let [page-id (mf/deref refs/workspace-page-id) + selected (mf/deref refs/selected-shapes) + shapes (->> (refs/objects-by-id selected) + (mf/deref) + (map gsh/transform-shape)) + selrect (gsh/selection-rect shapes) + frame-id (-> shapes first :frame-id) + frame (mf/deref (refs/object-by-id frame-id)) + zoom (mf/deref refs/selected-zoom) + current-transform (mf/deref refs/current-transform) + key (->> selected (map str) (str/join "-"))] + + (when (and (contains? layout :dynamic-alignment) + (= current-transform :move) + (not (empty? selected))) + [:g.distance + (for [coord [:x :y]] + [:& shape-distance + {:key (str key (name coord)) + :selrect selrect + :page-id page-id + :frame frame + :zoom zoom + :coord coord + :selected selected}])]))) diff --git a/frontend/src/uxbox/main/ui/workspace/snap_feedback.cljs b/frontend/src/uxbox/main/ui/workspace/snap_points.cljs similarity index 92% rename from frontend/src/uxbox/main/ui/workspace/snap_feedback.cljs rename to frontend/src/uxbox/main/ui/workspace/snap_points.cljs index 5beea4daf..4ec219936 100644 --- a/frontend/src/uxbox/main/ui/workspace/snap_feedback.cljs +++ b/frontend/src/uxbox/main/ui/workspace/snap_points.cljs @@ -1,4 +1,4 @@ -(ns uxbox.main.ui.workspace.snap-feedback +(ns uxbox.main.ui.workspace.snap-points (:require [rumext.alpha :as mf] [beicon.core :as rx] @@ -42,7 +42,7 @@ (rx/map #(vector point % coord))))) (rx/reduce conj []))) -(mf/defc snap-feedback-points +(mf/defc snap-feedback [{:keys [shapes page-id filter-shapes zoom] :as props}] (let [state (mf/use-state []) subject (mf/use-memo #(rx/subject)) @@ -84,7 +84,7 @@ :point point :zoom zoom}])])) -(mf/defc snap-feedback [{:keys [layout]}] +(mf/defc snap-points [{:keys [layout]}] (let [page-id (mf/deref refs/workspace-page-id) selected (mf/deref refs/selected-shapes) selected-shapes (mf/deref (refs/objects-by-id selected)) @@ -99,9 +99,10 @@ snap-data (mf/deref refs/workspace-snap-data) shapes (if drawing [drawing] selected-shapes) zoom (mf/deref refs/selected-zoom)] - (when (or drawing current-transform) - [:& snap-feedback-points {:shapes shapes - :page-id page-id - :filter-shapes filter-shapes - :zoom zoom}]))) + + (when (or drawing current-transform) + [:& snap-feedback {:shapes shapes + :page-id page-id + :filter-shapes filter-shapes + :zoom zoom}]))) diff --git a/frontend/src/uxbox/main/ui/workspace/viewport.cljs b/frontend/src/uxbox/main/ui/workspace/viewport.cljs index 99606351f..370f31219 100644 --- a/frontend/src/uxbox/main/ui/workspace/viewport.cljs +++ b/frontend/src/uxbox/main/ui/workspace/viewport.cljs @@ -28,7 +28,8 @@ [uxbox.main.ui.workspace.drawarea :refer [draw-area start-drawing]] [uxbox.main.ui.workspace.selection :refer [selection-handlers]] [uxbox.main.ui.workspace.presence :as presence] - [uxbox.main.ui.workspace.snap-feedback :refer [snap-feedback]] + [uxbox.main.ui.workspace.snap-points :refer [snap-points]] + [uxbox.main.ui.workspace.snap-distances :refer [snap-distances]] [uxbox.main.ui.workspace.frame-grid :refer [frame-grid]] [uxbox.common.math :as mth] [uxbox.util.dom :as dom] @@ -389,7 +390,8 @@ (when (contains? layout :display-grid) [:& frame-grid {:zoom zoom}]) - [:& snap-feedback {:layout layout}] + [:& snap-points {:layout layout}] + [:& snap-distances {:layout layout}] (when tooltip [:& cursor-tooltip {:zoom zoom :tooltip tooltip}])]