0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-15 11:38:24 -05:00

More functionality to dynamic alignment

This commit is contained in:
alonso.torres 2020-05-01 13:02:42 +02:00
parent ffd0c95760
commit 8cbc12ef94
10 changed files with 159 additions and 75 deletions

View file

@ -65,19 +65,21 @@
:bottom-left [ex sy])] :bottom-left [ex sy])]
(gpt/point x y))) (gpt/point x y)))
(defn finish-transform [state]
(update state :workspace-local dissoc :transform))
;; -- RESIZE ;; -- RESIZE
(defn start-resize (defn start-resize
[handler ids shape] [handler ids shape]
(letfn [(resize [shape initial [point lock?]] (letfn [(resize [shape initial resizing-shapes snap-data [point lock?]]
(let [{:keys [width height rotation]} shape (let [{:keys [width height rotation]} shape
shapev (-> (gpt/point width height)) shapev (-> (gpt/point width height))
;; Vector modifiers depending on the handler ;; Vector modifiers depending on the handler
handler-modif (let [[x y] (handler-modifiers handler)] (gpt/point x y)) handler-modif (let [[x y] (handler-modifiers handler)] (gpt/point x y))
;; Difference between the origin point in the coordinate system of the rotation ;; Difference between the origin point in the coordinate system of the rotation
deltav (-> (gpt/subtract point initial) deltav (-> (snap/closest-snap snap-data resizing-shapes (gpt/to-vec initial point))
(gpt/transform (gmt/rotate-matrix (- rotation))) (gpt/transform (gmt/rotate-matrix (- rotation)))
(gpt/multiply handler-modif)) (gpt/multiply handler-modif))
@ -115,27 +117,41 @@
;; (rx/of point))) ;; (rx/of point)))
] ]
(reify (reify
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc-in [:workspace-local :transform] :resize)))
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [initial (apply-zoom @ms/mouse-position) (let [initial (apply-zoom @ms/mouse-position)
shape (gsh/shape->rect-shape shape) shape (gsh/shape->rect-shape shape)
stoper (rx/filter ms/mouse-up? stream)] stoper (rx/filter ms/mouse-up? stream)
snap-data (get state :workspace-snap-data)
page-id (get state :current-page-id)
resizing-shapes (map #(get-in state [:workspace-data page-id :objects %]) ids)]
(rx/concat (rx/concat
(->> ms/mouse-position (->> ms/mouse-position
(rx/map apply-zoom) (rx/map apply-zoom)
;; (rx/mapcat apply-grid-alignment) ;; (rx/mapcat apply-grid-alignment)
(rx/with-latest vector ms/mouse-position-ctrl) (rx/with-latest vector ms/mouse-position-ctrl)
(rx/map normalize-proportion-lock) (rx/map normalize-proportion-lock)
(rx/mapcat (partial resize shape initial)) (rx/mapcat (partial resize shape initial resizing-shapes snap-data))
(rx/take-until stoper)) (rx/take-until stoper))
#_(rx/empty) #_(rx/empty)
(rx/of (apply-modifiers ids)))))))) (rx/of (apply-modifiers ids)
finish-transform)))))))
;; -- ROTATE ;; -- ROTATE
(defn start-rotate (defn start-rotate
[shapes] [shapes]
(ptk/reify ::start-rotate (ptk/reify ::start-rotate
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc-in [:workspace-local :transform] :rotate)))
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [stoper (rx/filter ms/mouse-up? stream) (let [stoper (rx/filter ms/mouse-up? stream)
@ -163,13 +179,19 @@
(let [delta-angle (calculate-angle pos ctrl?)] (let [delta-angle (calculate-angle pos ctrl?)]
(set-rotation delta-angle shapes group-center)))) (set-rotation delta-angle shapes group-center))))
(rx/take-until stoper)) (rx/take-until stoper))
(rx/of (apply-modifiers (map :id shapes)))))))) (rx/of (apply-modifiers (map :id shapes))
finish-transform))))))
;; -- MOVE ;; -- MOVE
(defn start-move-selected (defn start-move-selected
[] []
(ptk/reify ::start-move-selected (ptk/reify ::start-move-selected
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc-in [:workspace-local :transform] :move)))
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [selected (get-in state [:workspace-local :selected]) (let [selected (get-in state [:workspace-local :selected])
@ -192,11 +214,14 @@
(rx/map #(set-modifiers selected {:displacement %})) (rx/map #(set-modifiers selected {:displacement %}))
(rx/tap #(vswap! counter inc)) (rx/tap #(vswap! counter inc))
(rx/take-until stoper)) (rx/take-until stoper))
(->> (rx/create (fn [sink] (sink @counter))) (->> (rx/create (fn [sink] (sink (reduced @counter))))
(rx/mapcat (fn [n] (rx/mapcat (fn [n]
(if (zero? n) (if (zero? n)
(rx/empty) (rx/empty)
(rx/of (apply-modifiers selected))))))))))) (rx/of (apply-modifiers selected))))))
(rx/of finish-transform)
)))))
(defn- get-displacement-with-grid (defn- get-displacement-with-grid
"Retrieve the correct displacement delta point for the "Retrieve the correct displacement delta point for the

View file

@ -93,6 +93,15 @@
(def selected-shapes (def selected-shapes
(l/derived :selected workspace-local)) (l/derived :selected workspace-local))
(def selected-shapes-with-children
(letfn [(selector [state]
(let [selected (get-in state [:workspace-local :selected])
page-id (get-in state [:workspace-page :id])
objects (get-in state [:workspace-data page-id :objects])
children (mapcat #(helpers/get-children % objects) selected)]
(into selected children)))]
(l/derived selector st/state)))
(defn make-selected (defn make-selected
[id] [id]
(l/derived #(contains? % id) selected-shapes)) (l/derived #(contains? % id) selected-shapes))
@ -105,3 +114,6 @@
(def selected-edition (def selected-edition
(l/derived :edition workspace-local)) (l/derived :edition workspace-local))
(def current-transform
(l/derived :transform workspace-local))

View file

@ -119,8 +119,8 @@
(let [shape (get-in state [:workspace-local :drawing]) (let [shape (get-in state [:workspace-local :drawing])
shape (geom/setup shape {:x (:x point) shape (geom/setup shape {:x (:x point)
:y (:y point) :y (:y point)
:width 10 :width 1
:height 10})] :height 1})]
(assoc-in state [:workspace-local :drawing] (assoc shape ::initialized? true)))) (assoc-in state [:workspace-local :drawing] (assoc shape ::initialized? true))))
(resize-shape [{:keys [x y] :as shape} initial point lock?] (resize-shape [{:keys [x y] :as shape} initial point lock?]
@ -280,8 +280,16 @@
(rx/concat (rx/concat
(rx/of dw/clear-drawing) (rx/of dw/clear-drawing)
(when (::initialized? shape) (when (::initialized? shape)
(let [shape (-> shape (let [shape-min-width (case (:type shape)
:text 20
5)
shape-min-height (case (:type shape)
:text 16
5)
shape (-> shape
(geom/transform-shape) (geom/transform-shape)
(update :width #(max shape-min-width %))
(update :height #(max shape-min-height %))
(dissoc shape ::initialized?))] (dissoc shape ::initialized?))]
;; Add & select the created shape to the workspace ;; Add & select the created shape to the workspace
(rx/of dw/deselect-all (rx/of dw/deselect-all

View file

@ -22,7 +22,6 @@
[uxbox.util.object :as obj] [uxbox.util.object :as obj]
[uxbox.util.geom.point :as gpt] [uxbox.util.geom.point :as gpt]
[uxbox.util.geom.matrix :as gmt] [uxbox.util.geom.matrix :as gmt]
[uxbox.main.ui.workspace.snap-feedback :refer [snap-feedback]]
[uxbox.util.debug :refer [debug?]])) [uxbox.util.debug :refer [debug?]]))
;; --- Controls (Component) ;; --- Controls (Component)
@ -97,7 +96,7 @@
zoom (obj/get props "zoom") zoom (obj/get props "zoom")
on-resize (obj/get props "on-resize") on-resize (obj/get props "on-resize")
on-rotate (obj/get props "on-rotate") on-rotate (obj/get props "on-rotate")
current-transform (mf/deref refs/current-transform)
{:keys [x y width height rotation] :as shape} (geom/shape->rect-shape shape) {:keys [x y width height rotation] :as shape} (geom/shape->rect-shape shape)
radius (if (> (max width height) handler-size-threshold) 4.0 4.0) radius (if (> (max width height) handler-size-threshold) 4.0 4.0)
@ -112,23 +111,26 @@
:bottom-right [(+ x width) (+ y height)]}] :bottom-right [(+ x width) (+ y height)]}]
[:g.controls [:g.controls
[:rect.main {:transform transform (when (not (#{:move :rotate :resize} current-transform))
:x (- x 1) :y (- y 1) [:rect.main {:transform transform
:width (+ width 2) :x (- x 1) :y (- y 1)
:height (+ height 2) :width (+ width 2)
:style {:stroke "#1FDEA7" :height (+ height 2)
:stroke-width "1" :style {:stroke "#1FDEA7"
:fill "transparent"}}] :stroke-width "1"
:fill "transparent"}}])
(for [[position [cx cy]] resize-handlers] (when (not (#{:move :rotate} current-transform))
(let [tp (gpt/transform (gpt/point cx cy) transform)] (for [[position [cx cy]] resize-handlers]
[:* {:key (name position)} (let [tp (gpt/transform (gpt/point cx cy) transform)]
[:& rotation-handler {:cx (:x tp) [:* {:key (name position)}
:cy (:y tp) [:& rotation-handler {:key (str "rotation-" (name position))
:position position :cx (:x tp)
:rotation (:rotation shape) :cy (:y tp)
:zoom zoom :position position
:on-mouse-down on-rotate}] :rotation (:rotation shape)
:zoom zoom
:on-mouse-down on-rotate}]
[:& control-item {:class (name position) [:& control-item {:class (name position)
:on-click #(on-resize position %) :on-click #(on-resize position %)
@ -195,7 +197,6 @@
[{:keys [shapes selected zoom] :as props}] [{:keys [shapes selected zoom] :as props}]
(let [shape (geom/selection-rect shapes) (let [shape (geom/selection-rect shapes)
shape-center (geom/center shape) shape-center (geom/center shape)
on-resize #(do (dom/stop-propagation %2) on-resize #(do (dom/stop-propagation %2)
(st/emit! (dw/start-resize %1 selected shape))) (st/emit! (dw/start-resize %1 selected shape)))
@ -207,7 +208,6 @@
:zoom zoom :zoom zoom
:on-resize on-resize :on-resize on-resize
:on-rotate on-rotate}] :on-rotate on-rotate}]
[:& snap-feedback {:shapes shapes}]
(when (debug? :selection-center) (when (debug? :selection-center)
[:circle {:cx (:x shape-center) :cy (:y shape-center) :r 5 :fill "yellow"}])])) [:circle {:cx (:x shape-center) :cy (:y shape-center) :r 5 :fill "yellow"}])]))
@ -229,8 +229,7 @@
[:& controls {:shape shape' [:& controls {:shape shape'
:zoom zoom :zoom zoom
:on-rotate on-rotate :on-rotate on-rotate
:on-resize on-resize}] :on-resize on-resize}]]))
[:& snap-feedback {:shapes [shape]}]]))
(mf/defc selection-handlers (mf/defc selection-handlers
[{:keys [selected edition zoom] :as props}] [{:keys [selected edition zoom] :as props}]

View file

@ -1 +0,0 @@
alotor@bloodraven.68367:1587963441

View file

@ -5,44 +5,43 @@
[uxbox.util.geom.snap :as snap] [uxbox.util.geom.snap :as snap]
[uxbox.util.geom.point :as gpt])) [uxbox.util.geom.point :as gpt]))
(def ^:private line-color "#D383DA") (def ^:private line-color "#D383DA")
(mf/defc snap-feedback (mf/defc snap-feedback []
[{:keys [shapes] :as props}] (let [selected (mf/deref refs/selected-shapes)
(let [snap-data (mf/deref refs/workspace-snap-data)] shapes (mf/deref (refs/objects-by-id selected))
(for [shape shapes] filter-shapes (mf/deref refs/selected-shapes-with-children)
(for [point (snap/shape-snap-points shape)] current-transform (mf/deref refs/current-transform)
(let [frame-id (:frame-id shape) snap-data (mf/deref refs/workspace-snap-data)]
shape-id (:id shape) (when (not (nil? current-transform))
(for [shape shapes]
(for [point (snap/shape-snap-points shape)]
(let [frame-id (:frame-id shape)
shape-id (:id shape)
snaps (into #{}
(concat
(snap/get-snap-points snap-data frame-id filter-shapes point :x)
(snap/get-snap-points snap-data frame-id filter-shapes point :y)))]
(if (not-empty snaps)
[:* {:key (str "point-" (:id shape) "-" (:x point) "-" (:y point))}
[:circle {:cx (:x point)
:cy (:y point)
:r 2
:fill line-color}]
snaps-x (snap/get-snap-points snap-data frame-id shape-id point :x) (for [snap snaps]
snaps-y (snap/get-snap-points snap-data frame-id shape-id point :y)] [:circle {:key (str "snap-" (:id shape) "-" (:x point) "-" (:y point) "-" (:x snap) "-" (:y snap))
(if (or (not-empty snaps-x) (not-empty snaps-y)) :cx (:x snap)
[:* {:key (str "point-" (:id shape) "-" (:x point) "-" (:y point))}
[:circle {:cx (:x point)
:cy (:y point)
:r 2
:fill line-color}]
(for [snap (concat snaps-x snaps-y)]
[:*
[:circle {:cx (:x snap)
:cy (:y snap) :cy (:y snap)
:r 2 :r 2
:fill line-color}] :fill line-color}])
[:line {:x1 (:x snap)
(for [snap snaps]
[:line {:key (str "line-" (:id shape) "-" (:x point) "-" (:y point) "-" (:x snap) "-" (:y snap))
:x1 (:x snap)
:y1 (:y snap) :y1 (:y snap)
:x2 (:x point) :x2 (:x point)
:y2 (:y point) :y2 (:y point)
:style {:stroke line-color :stroke-width "1"} :style {:stroke line-color :stroke-width "1"}
:opacity 0.4}]]) :opacity 0.4}])])))))))
#_(when is-snap-y?
[:line {:x1 -10000
:y1 (:y point)
:x2 10000
:y2 (:y point)
:style {:stroke line-color :stroke-width "1"}
:opacity 0.4}])]))))))

View file

@ -29,6 +29,7 @@
[uxbox.main.ui.workspace.ruler :refer [ruler]] [uxbox.main.ui.workspace.ruler :refer [ruler]]
[uxbox.main.ui.workspace.selection :refer [selection-handlers]] [uxbox.main.ui.workspace.selection :refer [selection-handlers]]
[uxbox.main.ui.workspace.presence :as presence] [uxbox.main.ui.workspace.presence :as presence]
[uxbox.main.ui.workspace.snap-feedback :refer [snap-feedback]]
[uxbox.util.dom :as dom] [uxbox.util.dom :as dom]
[uxbox.util.geom.point :as gpt] [uxbox.util.geom.point :as gpt]
[uxbox.util.perf :as perf] [uxbox.util.perf :as perf]
@ -305,6 +306,8 @@
:zoom zoom :zoom zoom
:edition edition}]) :edition edition}])
[:& snap-feedback]
(when-let [drawing-shape (:drawing local)] (when-let [drawing-shape (:drawing local)]
[:& draw-area {:shape drawing-shape [:& draw-area {:shape drawing-shape
:zoom zoom :zoom zoom

View file

@ -8,8 +8,9 @@
;; Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz> ;; Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.util.geom.point (ns uxbox.util.geom.point
(:refer-clojure :exclude [divide]) (:refer-clojure :exclude [divide min max])
(:require (:require
[cljs.core :as c]
[cuerdas.core :as str] [cuerdas.core :as str]
[uxbox.util.math :as mth] [uxbox.util.math :as mth]
[cognitect.transit :as t])) [cognitect.transit :as t]))
@ -70,6 +71,15 @@
(assert (point? other)) (assert (point? other))
(Point. (/ x ox) (/ y oy))) (Point. (/ x ox) (/ y oy)))
(defn min
[{x1 :x y1 :y :as p1} {x2 :x y2 :y :as p2}]
(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)))
(defn inverse (defn inverse
[{:keys [x y] :as p}] [{:keys [x y] :as p}]
(assert (point? p)) (assert (point? p))

View file

@ -738,6 +738,15 @@
(gpt/divide (gpt/point (:width shape-path-temp-rec) (:height shape-path-temp-rec)) (gpt/divide (gpt/point (:width shape-path-temp-rec) (:height shape-path-temp-rec))
(gpt/point (:width shape-path-temp-dim) (:height shape-path-temp-dim))))) (gpt/point (:width shape-path-temp-dim) (:height shape-path-temp-dim)))))
(defn- fix-invalid-rect-values [rect-shape]
(letfn [(check [num] (if (or (nil? num) (mth/nan? num)) 0 num))
(to-positive [num] (if (< num 1) 1 num))]
(-> rect-shape
(update :x check)
(update :y check)
(update :width (comp to-positive check))
(update :height (comp to-positive check)))))
(defn transform-rect-shape (defn transform-rect-shape
[shape] [shape]
(let [;; Apply modifiers to the rect as a path so we have the end shape expected (let [;; Apply modifiers to the rect as a path so we have the end shape expected
@ -785,6 +794,7 @@
(merge rec) (merge rec)
(update :x #(mth/precision % 2)) (update :x #(mth/precision % 2))
(update :y #(mth/precision % 2)) (update :y #(mth/precision % 2))
(fix-invalid-rect-values)
(update :transform #(gmt/multiply (or % (gmt/matrix)) stretch-matrix)) (update :transform #(gmt/multiply (or % (gmt/matrix)) stretch-matrix))
(update :transform-inverse #(gmt/multiply stretch-matrix-inverse (or % (gmt/matrix)))))] (update :transform-inverse #(gmt/multiply stretch-matrix-inverse (or % (gmt/matrix)))))]

View file

@ -16,17 +16,29 @@
[uxbox.util.geom.point :as gpt] [uxbox.util.geom.point :as gpt]
[uxbox.util.debug :refer [logjs]])) [uxbox.util.debug :refer [logjs]]))
(def ^:private snap-accuracy 5) (def ^:private snap-accuracy 8)
(defn mapm (defn mapm
"Map over the values of a map" "Map over the values of a map"
[mfn coll] [mfn coll]
(into {} (map (fn [[key val]] [key (mfn val)]) coll))) (into {} (map (fn [[key val]] [key (mfn val)]) coll)))
(defn- frame-snap-points [{:keys [x y width height]}]
#{(gpt/point x y)
(gpt/point (+ x (/ width 2)) y)
(gpt/point (+ x width) y)
(gpt/point (+ x width) (+ y (/ height 2)))
(gpt/point (+ x width) (+ y height))
(gpt/point (+ x (/ width 2)) (+ y height))
(gpt/point x (+ y height))
(gpt/point x (+ y (/ height 2)))})
(defn shape-snap-points [shape] (defn shape-snap-points [shape]
(let [modified-path (gsh/transform-apply-modifiers shape) (if (= :frame (:type shape))
shape-center (gsh/center modified-path)] (frame-snap-points shape)
(into #{shape-center} (:segments modified-path)))) (let [modified-path (gsh/transform-apply-modifiers shape)
shape-center (gsh/center modified-path)]
(into #{shape-center} (:segments modified-path)))))
(defn create-coord-data [shapes coord] (defn create-coord-data [shapes coord]
(let [process-shape (let [process-shape
@ -42,7 +54,14 @@
"Initialize the snap information with the current workspace information" "Initialize the snap information with the current workspace information"
[objects] [objects]
(let [shapes (vals objects) (let [shapes (vals objects)
frame-shapes (group-by :frame-id (filter (comp not nil? :frame-id) shapes))] frame-shapes (->> shapes
(filter (comp not nil? :frame-id))
(group-by :frame-id))
frame-shapes (->> shapes
(filter #(= :frame (:type %)))
(remove #(= zero (:id %)))
(reduce #(update %1 (:id %2) conj %2) frame-shapes))]
(logjs "snap-data" (logjs "snap-data"
(mapm (fn [shapes] {:x (create-coord-data shapes :x) (mapm (fn [shapes] {:x (create-coord-data shapes :x)
:y (create-coord-data shapes :y)}) :y (create-coord-data shapes :y)})
@ -125,13 +144,13 @@
(gpt/add trans-vec snapv)))) (gpt/add trans-vec snapv))))
(defn get-snap-points [snap-data frame-id shape-id point coord] (defn get-snap-points [snap-data frame-id filter-shapes point coord]
(let [value (coord point) (let [value (coord point)
;; Search for values within 1 pixel ;; Search for values within 1 pixel
snap-matches (-> (get-in snap-data [frame-id coord]) snap-matches (-> (get-in snap-data [frame-id coord])
(range-query (- value 0.5) (+ value 0.5)) (range-query (- value 0.5) (+ value 0.5))
(remove-from-snap-points #{shape-id})) (remove-from-snap-points filter-shapes))
snap-points (mapcat (fn [[v data]] (map (fn [[point _]] point) data)) snap-matches)] snap-points (mapcat (fn [[v data]] (map (fn [[point _]] point) data)) snap-matches)]
snap-points)) snap-points))
@ -139,5 +158,5 @@
(defn is-snapping? [snap-data frame-id shape-id point coord] (defn is-snapping? [snap-data frame-id shape-id point coord]
(let [value (coord point) (let [value (coord point)
;; Search for values within 1 pixel ;; Search for values within 1 pixel
snap-points (range-query (get-in snap-data [frame-id coord]) (- value 0.25) (+ value 0.25))] snap-points (range-query (get-in snap-data [frame-id coord]) (- value 1.0) (+ value 1.0))]
(some (fn [[point other-shape-id]] (not (= shape-id other-shape-id))) snap-points))) (some (fn [[point other-shape-id]] (not (= shape-id other-shape-id))) snap-points)))