0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-10 17:18:21 -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])]
(gpt/point x y)))
(defn finish-transform [state]
(update state :workspace-local dissoc :transform))
;; -- RESIZE
(defn start-resize
[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
shapev (-> (gpt/point width height))
;; Vector modifiers depending on the handler
handler-modif (let [[x y] (handler-modifiers handler)] (gpt/point x y))
;; 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/multiply handler-modif))
@ -115,27 +117,41 @@
;; (rx/of point)))
]
(reify
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc-in [:workspace-local :transform] :resize)))
ptk/WatchEvent
(watch [_ state stream]
(let [initial (apply-zoom @ms/mouse-position)
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
(->> ms/mouse-position
(rx/map apply-zoom)
;; (rx/mapcat apply-grid-alignment)
(rx/with-latest vector ms/mouse-position-ctrl)
(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/empty)
(rx/of (apply-modifiers ids))))))))
(rx/of (apply-modifiers ids)
finish-transform)))))))
;; -- ROTATE
(defn start-rotate
[shapes]
(ptk/reify ::start-rotate
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc-in [:workspace-local :transform] :rotate)))
ptk/WatchEvent
(watch [_ state stream]
(let [stoper (rx/filter ms/mouse-up? stream)
@ -163,13 +179,19 @@
(let [delta-angle (calculate-angle pos ctrl?)]
(set-rotation delta-angle shapes group-center))))
(rx/take-until stoper))
(rx/of (apply-modifiers (map :id shapes))))))))
(rx/of (apply-modifiers (map :id shapes))
finish-transform))))))
;; -- MOVE
(defn start-move-selected
[]
(ptk/reify ::start-move-selected
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc-in [:workspace-local :transform] :move)))
ptk/WatchEvent
(watch [_ state stream]
(let [selected (get-in state [:workspace-local :selected])
@ -192,11 +214,14 @@
(rx/map #(set-modifiers selected {:displacement %}))
(rx/tap #(vswap! counter inc))
(rx/take-until stoper))
(->> (rx/create (fn [sink] (sink @counter)))
(->> (rx/create (fn [sink] (sink (reduced @counter))))
(rx/mapcat (fn [n]
(if (zero? n)
(rx/empty)
(rx/of (apply-modifiers selected)))))))))))
(rx/of (apply-modifiers selected))))))
(rx/of finish-transform)
)))))
(defn- get-displacement-with-grid
"Retrieve the correct displacement delta point for the

View file

@ -93,6 +93,15 @@
(def selected-shapes
(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
[id]
(l/derived #(contains? % id) selected-shapes))
@ -105,3 +114,6 @@
(def selected-edition
(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])
shape (geom/setup shape {:x (:x point)
:y (:y point)
:width 10
:height 10})]
:width 1
:height 1})]
(assoc-in state [:workspace-local :drawing] (assoc shape ::initialized? true))))
(resize-shape [{:keys [x y] :as shape} initial point lock?]
@ -280,8 +280,16 @@
(rx/concat
(rx/of dw/clear-drawing)
(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)
(update :width #(max shape-min-width %))
(update :height #(max shape-min-height %))
(dissoc shape ::initialized?))]
;; Add & select the created shape to the workspace
(rx/of dw/deselect-all

View file

@ -22,7 +22,6 @@
[uxbox.util.object :as obj]
[uxbox.util.geom.point :as gpt]
[uxbox.util.geom.matrix :as gmt]
[uxbox.main.ui.workspace.snap-feedback :refer [snap-feedback]]
[uxbox.util.debug :refer [debug?]]))
;; --- Controls (Component)
@ -97,7 +96,7 @@
zoom (obj/get props "zoom")
on-resize (obj/get props "on-resize")
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)
radius (if (> (max width height) handler-size-threshold) 4.0 4.0)
@ -112,18 +111,21 @@
:bottom-right [(+ x width) (+ y height)]}]
[:g.controls
(when (not (#{:move :rotate :resize} current-transform))
[:rect.main {:transform transform
:x (- x 1) :y (- y 1)
:width (+ width 2)
:height (+ height 2)
:style {:stroke "#1FDEA7"
:stroke-width "1"
:fill "transparent"}}]
:fill "transparent"}}])
(when (not (#{:move :rotate} current-transform))
(for [[position [cx cy]] resize-handlers]
(let [tp (gpt/transform (gpt/point cx cy) transform)]
[:* {:key (name position)}
[:& rotation-handler {:cx (:x tp)
[:& rotation-handler {:key (str "rotation-" (name position))
:cx (:x tp)
:cy (:y tp)
:position position
:rotation (:rotation shape)
@ -195,7 +197,6 @@
[{:keys [shapes selected zoom] :as props}]
(let [shape (geom/selection-rect shapes)
shape-center (geom/center shape)
on-resize #(do (dom/stop-propagation %2)
(st/emit! (dw/start-resize %1 selected shape)))
@ -207,7 +208,6 @@
:zoom zoom
:on-resize on-resize
:on-rotate on-rotate}]
[:& snap-feedback {:shapes shapes}]
(when (debug? :selection-center)
[:circle {:cx (:x shape-center) :cy (:y shape-center) :r 5 :fill "yellow"}])]))
@ -229,8 +229,7 @@
[:& controls {:shape shape'
:zoom zoom
:on-rotate on-rotate
:on-resize on-resize}]
[:& snap-feedback {:shapes [shape]}]]))
:on-resize on-resize}]]))
(mf/defc selection-handlers
[{: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.point :as gpt]))
(def ^:private line-color "#D383DA")
(mf/defc snap-feedback
[{:keys [shapes] :as props}]
(let [snap-data (mf/deref refs/workspace-snap-data)]
(mf/defc snap-feedback []
(let [selected (mf/deref refs/selected-shapes)
shapes (mf/deref (refs/objects-by-id selected))
filter-shapes (mf/deref refs/selected-shapes-with-children)
current-transform (mf/deref refs/current-transform)
snap-data (mf/deref refs/workspace-snap-data)]
(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-x (snap/get-snap-points snap-data frame-id shape-id point :x)
snaps-y (snap/get-snap-points snap-data frame-id shape-id point :y)]
(if (or (not-empty snaps-x) (not-empty snaps-y))
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}]
(for [snap (concat snaps-x snaps-y)]
[:*
[:circle {:cx (:x snap)
(for [snap snaps]
[:circle {:key (str "snap-" (:id shape) "-" (:x point) "-" (:y point) "-" (:x snap) "-" (:y snap))
:cx (:x snap)
:cy (:y snap)
:r 2
:fill line-color}]
[:line {:x1 (:x snap)
:fill line-color}])
(for [snap snaps]
[:line {:key (str "line-" (:id shape) "-" (:x point) "-" (:y point) "-" (:x snap) "-" (:y snap))
:x1 (:x snap)
:y1 (:y snap)
:x2 (:x point)
:y2 (:y point)
:style {:stroke line-color :stroke-width "1"}
: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}])]))))))
:opacity 0.4}])])))))))

View file

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

View file

@ -8,8 +8,9 @@
;; Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.util.geom.point
(:refer-clojure :exclude [divide])
(:refer-clojure :exclude [divide min max])
(:require
[cljs.core :as c]
[cuerdas.core :as str]
[uxbox.util.math :as mth]
[cognitect.transit :as t]))
@ -70,6 +71,15 @@
(assert (point? other))
(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
[{:keys [x y] :as 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/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
[shape]
(let [;; Apply modifiers to the rect as a path so we have the end shape expected
@ -785,6 +794,7 @@
(merge rec)
(update :x #(mth/precision % 2))
(update :y #(mth/precision % 2))
(fix-invalid-rect-values)
(update :transform #(gmt/multiply (or % (gmt/matrix)) stretch-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.debug :refer [logjs]]))
(def ^:private snap-accuracy 5)
(def ^:private snap-accuracy 8)
(defn mapm
"Map over the values of a map"
[mfn 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]
(if (= :frame (:type shape))
(frame-snap-points shape)
(let [modified-path (gsh/transform-apply-modifiers shape)
shape-center (gsh/center modified-path)]
(into #{shape-center} (:segments modified-path))))
(into #{shape-center} (:segments modified-path)))))
(defn create-coord-data [shapes coord]
(let [process-shape
@ -42,7 +54,14 @@
"Initialize the snap information with the current workspace information"
[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"
(mapm (fn [shapes] {:x (create-coord-data shapes :x)
:y (create-coord-data shapes :y)})
@ -125,13 +144,13 @@
(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)
;; Search for values within 1 pixel
snap-matches (-> (get-in snap-data [frame-id coord])
(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))
@ -139,5 +158,5 @@
(defn is-snapping? [snap-data frame-id shape-id point coord]
(let [value (coord point)
;; 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)))