0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-26 08:45:34 -05:00

🎉 Changed the user origin for shapes to their corner

This commit is contained in:
alonso.torres 2020-04-29 09:59:12 +02:00
parent db4e0fc314
commit 7ab3d86bc6
19 changed files with 195 additions and 182 deletions

View file

@ -88,7 +88,3 @@
(when (= "main" (unchecked-get js/window app-sym))
(reinit)))
(defn ^:export toggle-debug
[]
(swap! st/*debug* not))

View file

@ -70,7 +70,6 @@
(let [frame (get objects (:frame-id shape))
{:keys [width height rotation]} shape
center (gpt/center shape)
shapev (-> (gpt/point width height))
;; Vector modifiers depending on the handler
@ -140,7 +139,7 @@
(watch [_ state stream]
(let [stoper (rx/filter ms/mouse-up? stream)
group (gsh/selection-rect shapes)
group-center (gpt/center group)
group-center (gsh/center group)
initial-angle (gpt/angle (apply-zoom @ms/mouse-position) group-center)
calculate-angle (fn [pos ctrl?]
(let [angle (- (gpt/angle pos group-center) initial-angle)
@ -161,7 +160,7 @@
(rx/with-latest vector ms/mouse-position-ctrl)
(rx/map (fn [[pos ctrl?]]
(let [delta-angle (calculate-angle pos ctrl?)]
(set-rotation delta-angle shapes))))
(set-rotation delta-angle shapes group-center))))
(rx/take-until stoper))
(rx/of (apply-modifiers (map :id shapes))))))))
@ -275,7 +274,7 @@
;; Set-rotation is custom because applies different modifiers to each shape adjusting their position
(defn set-rotation
[delta-rotation shapes]
[delta-rotation shapes center]
(ptk/reify ::set-rotation
IUpdateGroup
(get-ids [_] (map :id shapes))
@ -293,8 +292,7 @@
(rotate-around-center [state angle center shapes]
(reduce #(rotate-shape %1 angle %2 center) state shapes))]
(let [center (-> shapes gsh/selection-rect gpt/center)
objects (get-in state [:workspace-data page-id :objects])
(let [objects (get-in state [:workspace-data page-id :objects])
id->obj #(get objects %)
get-children (fn [shape] (map id->obj (helpers/get-children (:id shape) objects)))
shapes (concat shapes (mapcat get-children shapes))]

View file

@ -57,6 +57,14 @@
(get-in % [:workspace-data page-id]))
(l/derived st/state)))
(defn object-by-id
[id]
(letfn [(selector [state]
(let [page-id (get-in state [:workspace-page :id])
objects (get-in state [:workspace-data page-id :objects])]
(->> (get objects id))))]
(l/derived selector st/state =)))
(defn objects-by-id
[ids]
(letfn [(selector [state]

View file

@ -23,11 +23,10 @@
(when (debug? :bounding-boxes)
(let [shape (unchecked-get props "shape")
frame (unchecked-get props "frame")
selrect (geom/transform-selrect frame shape)
shape-path (-> shape
(geom/transform-apply-modifiers)
selrect (-> shape
(geom/selection-rect-shape)
(geom/translate-to-frame frame))
shape-center (geom/center shape-path)]
shape-center (geom/center selrect)]
[:g
[:text {:x (:x selrect)
:y (- (:y selrect) 5)
@ -37,10 +36,12 @@
:stroke-width 0.1}
(str/format "%s - (%s, %s)" (str/slice (str (:id shape)) 0 8) (fix (:x shape)) (fix (:y shape)))]
[:circle {:cx (:x shape-center) :cy (:y shape-center) :r 5 :fill "yellow"}]
[:rect {:x (:x selrect)
:y (:y selrect)
:width (:width selrect)
:height (:height selrect)
:style {:stroke "red" :fill "transparent" :stroke-width "1px" :stroke-opacity 0.5}}]])))
:style {:stroke "red"
:fill "transparent"
:stroke-width "1px"
:stroke-opacity 0.5
:pointer-events "none"}}]])))

View file

@ -22,7 +22,7 @@
(declare circle-shape)
(mf/defc circle-wrapper
[{:keys [shape frame] :as props}]
[{:keys [shape] :as props}]
(let [selected (mf/deref refs/selected-shapes)
selected? (contains? selected (:id shape))
on-mouse-down #(common/on-mouse-down % shape)
@ -30,8 +30,7 @@
[:g.shape {:class (when selected? "selected")
:on-mouse-down on-mouse-down
:on-context-menu on-context-menu}
[:& circle-shape {:shape (geom/transform-shape frame shape)}]
[:& bounding-box {:shape shape :frame frame}]]))
[:& circle-shape {:shape shape}]]))
;; --- Circle Shape

View file

@ -75,7 +75,7 @@
inv-zoom (/ 1 zoom)
childs (mapv #(get objects %) (:shapes shape))
ds-modifier (:displacement-modifier shape)
ds-modifier (get-in shape [:modifiers :displacement])
label-pos (cond-> (gpt/point x (- y 10))
(gmt/matrix? ds-modifier) (gpt/transform ds-modifier))

View file

@ -73,11 +73,9 @@
[:& group-shape
{:frame frame
:shape (geom/transform-shape frame shape)
:shape shape
:children children
:is-child-selected? is-child-selected?}]
(when (not is-child-selected?)
[:& bounding-box {:shape shape :frame frame}])]))))
:is-child-selected? is-child-selected?}]]))))
(defn group-shape
[shape-wrapper]

View file

@ -15,15 +15,14 @@
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.main.ui.shapes.common :as common]
[uxbox.util.interop :as itr]
[uxbox.util.geom.matrix :as gmt]
[uxbox.main.ui.shapes.bounding-box :refer [bounding-box]]))
[uxbox.util.geom.matrix :as gmt]))
;; --- Image Wrapper
(declare image-shape)
(mf/defc image-wrapper
[{:keys [shape frame] :as props}]
[{:keys [shape] :as props}]
(let [selected (mf/deref refs/selected-shapes)
selected? (contains? selected (:id shape))
on-mouse-down (mf/use-callback
@ -37,8 +36,7 @@
[:g.shape {:class (when selected? "selected")
:on-mouse-down on-mouse-down
:on-context-menu on-context-menu}
[:& image-shape {:shape (geom/transform-shape frame shape)}]
[:& bounding-box {:shape shape :frame frame}]]))
[:& image-shape {:shape shape}]]))
;; --- Image Shape

View file

@ -24,7 +24,7 @@
(declare path-shape)
(mf/defc path-wrapper
[{:keys [shape frame] :as props}]
[{:keys [shape] :as props}]
(let [selected (mf/deref refs/selected-shapes)
selected? (contains? selected (:id shape))
on-mouse-down (mf/use-callback
@ -42,8 +42,7 @@
[:g.shape {:on-double-click on-double-click
:on-mouse-down on-mouse-down
:on-context-menu on-context-menu}
[:& path-shape {:shape (geom/transform-shape frame shape) :background? true}]
[:& bounding-box {:shape shape :frame frame}]]))
[:& path-shape {:shape shape :background? true}]]))
;; --- Path Shape

View file

@ -15,7 +15,6 @@
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.main.ui.shapes.common :as common]
[uxbox.util.interop :as itr]
[uxbox.main.ui.shapes.bounding-box :refer [bounding-box]]
[uxbox.main.ui.shapes.custom-stroke :refer [shape-custom-stroke]]))
;; --- Rect Wrapper
@ -26,7 +25,6 @@
{::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
frame (unchecked-get props "frame")
on-mouse-down (mf/use-callback
(mf/deps shape)
#(common/on-mouse-down % shape))
@ -35,8 +33,7 @@
#(common/on-context-menu % shape))]
[:g.shape {:on-mouse-down on-mouse-down
:on-context-menu on-context-menu}
[:& rect-shape {:shape (geom/transform-shape frame shape) }]
[:& bounding-box {:shape shape :frame frame}]]))
[:& rect-shape {:shape shape}]]))
;; --- Rect Shape

View file

@ -19,6 +19,8 @@
[uxbox.main.ui.shapes.text :as text]
[uxbox.main.ui.shapes.group :as group]
[uxbox.main.ui.shapes.frame :as frame]
[uxbox.main.ui.shapes.bounding-box :refer [bounding-box]]
[uxbox.util.geom.shapes :as gsh]
[uxbox.main.refs :as refs]))
(defn- shape-wrapper-memo-equals?
@ -42,8 +44,10 @@
[props]
(let [shape (unchecked-get props "shape")
frame (unchecked-get props "frame")
opts #js {:shape shape :frame frame}]
opts #js {:shape (->> shape (gsh/transform-shape frame))
:frame frame}]
(when (and shape (not (:hidden shape)))
[:*
(case (:type shape)
:group [:> group-wrapper opts]
:curve [:> path/path-wrapper opts]
@ -55,8 +59,9 @@
:circle [:> circle/circle-wrapper opts]
;; Only used when drawing a new frame.
:frame [:> frame-wrapper opts]
nil))))
:frame [:> frame-wrapper {:shape shape}]
nil)
[:& bounding-box {:shape shape :frame frame}]])))
(def group-wrapper (group/group-wrapper shape-wrapper))
(def frame-wrapper (frame/frame-wrapper shape-wrapper))

View file

@ -46,7 +46,7 @@
(declare text-shape)
(mf/defc text-wrapper
[{:keys [shape frame] :as props}]
[{:keys [shape] :as props}]
(let [{:keys [id x1 y1 content group]} shape
selected (mf/deref refs/selected-shapes)
edition (mf/deref refs/selected-edition)
@ -67,8 +67,8 @@
:on-mouse-down on-mouse-down
:on-context-menu on-context-menu}
(if edition?
[:& text-shape-edit {:shape (geom/transform-shape frame shape)}]
[:& text-shape {:shape (geom/transform-shape frame shape)
[:& text-shape-edit {:shape shape}]
[:& text-shape {:shape shape
:selected? selected?}])]))
;; --- Text Rendering
@ -284,7 +284,8 @@
(mf/use-effect on-mount)
[:foreignObject {:x x :y y :width width :height height :ref self-ref}
[:foreignObject {:transform (geom/transform-matrix shape)
:x x :y y :width width :height height :ref self-ref}
[:> rslate/Slate {:editor editor
:value @state
:on-change on-change}
@ -305,13 +306,7 @@
(mf/defc text-shape
[{:keys [shape selected?] :as props}]
(let [{:keys [id x y width height rotation content]} shape
transform (when (and rotation (pos? rotation))
(str/format "rotate(%s %s %s)"
rotation
(+ x (/ width 2))
(+ y (/ height 2))))
(let [{:keys [id x y width height content]} shape
content (parse-content content)
editor (mf/use-memo #(rslate/withReact (slate/createEditor)))
@ -334,7 +329,7 @@
[:foreignObject {:x x
:y y
:transform transform
:transform (geom/transform-matrix shape)
:id (str id)
:width width
:height height}

View file

@ -83,7 +83,7 @@
(let [modifier (-> (gpt/point (:x frame) (:y frame))
(gpt/negate)
(gmt/translate-matrix))
frame (assoc frame :displacement-modifier modifier)
frame (assoc-in frame [:modifiers :displacement] modifier )
width (* (:width frame) zoom)
height (* (:height frame) zoom)

View file

@ -305,7 +305,7 @@
(mf/defc generic-draw-area
[{:keys [shape zoom]}]
(let [{:keys [x y width height]} (geom/transform-selrect nil shape)]
(let [{:keys [x y width height]} (geom/selection-rect-shape shape)]
(when (and x y)
[:g
[:& shapes/shape-wrapper {:shape shape}]

View file

@ -72,6 +72,7 @@
[{:keys [shape zoom on-resize on-rotate] :as props}]
(let [{: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)
transform (geom/transform-matrix shape)
resize-handlers {:top [(+ x (/ width 2 )) y]
@ -85,14 +86,12 @@
[:g.controls
[:rect.main {:transform transform
:x x :y y
:width width
:height height
;;:stroke-dasharray (str (/ 8.0 zoom) "," (/ 5 zoom))
:vector-effect "non-scaling-stroke"
:x (- x 1) :y (- y 1)
:width (+ width 2)
:height (+ height 2)
:style {:stroke "#1FDEA7"
:fill "transparent"
:stroke-opacity "1"}}]
:stroke-width "1"
:fill "transparent"}}]
(for [[position [cx cy]] resize-handlers]
(let [tp (gpt/transform (gpt/point cx cy) transform)]
@ -156,12 +155,12 @@
(mf/defc text-edition-selection-handlers
[{:keys [shape zoom] :as props}]
(let [{:keys [x y width height] :as shape} shape]
(let [{:keys [x y width height]} shape]
[:g.controls
[:rect.main {:x x :y y
:transform (geom/transform-matrix shape)
:width width
:height height
;; :stroke-dasharray (str (/ 5.0 zoom) "," (/ 5 zoom))
:style {:stroke "#1FDEA7"
:stroke-width "0.5"
:stroke-opacity "1"
@ -170,16 +169,20 @@
(mf/defc multiple-selection-handlers
[{:keys [shapes selected zoom objects] :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 objects)))
on-rotate #(do (dom/stop-propagation %)
(st/emit! (dw/start-rotate shapes)))]
[:*
[:& controls {:shape shape
:zoom zoom
:on-resize on-resize
:on-rotate on-rotate}]))
:on-rotate on-rotate}]
(when (debug? :selection-center)
[:circle {:cx (:x shape-center) :cy (:y shape-center) :r 5 :fill "yellow"}])]))
(mf/defc single-selection-handlers
[{:keys [shape zoom objects] :as props}]

View file

@ -15,25 +15,43 @@
[uxbox.main.refs :as refs]
[uxbox.common.data :as d]
[uxbox.util.dom :as dom]
[uxbox.util.geom.shapes :as gsh]
[uxbox.util.geom.point :as gpt]
[uxbox.main.data.workspace :as udw]
[uxbox.util.math :as math]
[uxbox.util.i18n :refer [t] :as i18n]))
;; -- User/drawing coords
(defn user-coords-vector [shape]
(let [{sel-x :x sel-y :y :as selrect}
(-> shape
gsh/shape->path
(gsh/center-transform (:transform shape))
gsh/shape->rect-shape)
{rec-x :x rec-y :y} (-> shape gsh/shape->rect-shape)
dx (- rec-x sel-x)
dy (- rec-y sel-y)]
(gpt/point dx dy)))
(defn user->draw [{:keys [x y width height] :as shape}]
(let [dv (user-coords-vector shape)]
(-> shape (gsh/move dv))))
(defn draw->user [{:keys [x y width height] :as shape}]
(let [dv (user-coords-vector shape)]
(-> shape (gsh/move (gpt/negate dv)))))
(mf/defc measures-menu
[{:keys [shape options] :as props}]
(let [options (or options #{:size :position :rotation :radius})
locale (i18n/use-locale)
frame (deref (refs/object-by-id (:frame-id shape)))
data (deref refs/workspace-data)
parent (get-in data [:objects (:frame-id shape)])
x (cond
(:x shape) :x
(:cx shape) :cx)
y (cond
(:y shape) :y
(:cy shape) :cy)
shape (->> shape
(gsh/transform-shape frame)
(draw->user))
on-size-change
(fn [event attr]
@ -58,9 +76,12 @@
(fn [event attr]
(let [value (-> (dom/get-target event)
(dom/get-value)
(d/parse-integer 0)
(+ (attr parent)))] ; Convert back to absolute position before update
(st/emit! (udw/update-position (:id shape) {attr value}))))
(d/parse-integer 0))
new-shape (-> shape
(assoc attr value)
(gsh/translate-from-frame frame)
(user->draw))]
(st/emit! (udw/update-position (:id shape) (select-keys new-shape [attr])))))
on-rotation-change
(fn [event]
@ -148,17 +169,21 @@
:type "number"
:no-validate true
:on-change on-pos-x-change
:value (str (-> (- (x shape) (:x parent)) ; Show to user position relative to frame
(d/coalesce 0)
(math/round)))}]]
:value (:x shape)
;;:value (str (-> (- (x shape) (:x parent)) ; Show to user position relative to frame
;; (d/coalesce 0)
;; (math/round)))
}]]
[:div.input-element.pixels
[:input.input-text {:placeholder "y"
:type "number"
:no-validate true
:on-change on-pos-y-change
:value (str (-> (- (y shape) (:y parent))
(d/coalesce 0)
(math/round)))}]]])
:value (:y shape)
;;:value (str (-> (- (y shape) (:y parent))
;; (d/coalesce 0)
;; (math/round)))
}]]])
(when (options :rotation)
[:div.row-flex

View file

@ -1,9 +1,7 @@
(ns uxbox.util.debug
"Debugging utils"
(:require
[uxbox.main.store :as store]))
"Debugging utils")
(def debug-options #{:bounding-boxes :group :events :rotation-handler #_:simple-selection })
(def debug-options #{:bounding-boxes :group :events :rotation-handler :selection-center #_:simple-selection })
(defonce ^:dynamic *debug* (atom #{}))
@ -13,6 +11,13 @@
(defn -debug! [option] (swap! *debug* disj option))
(defn debug? [option] (@*debug* option))
(defn ^:export toggle-debug [name] (let [option (keyword name)]
(if (debug? option)
(-debug! option)
(debug! option))))
(defn ^:export debug-all [name] (debug-all!))
(defn tap
"Transducer function that can execute a side-effect `effect-fn` per input"
[effect-fn]
@ -31,9 +36,4 @@
(js/console.log str (clj->js val))
val))
(defn dump-state []
(logjs "state" @store/state))
(defn dump-objects []
(let [page-id (get @store/state :current-page-id)]
(logjs "state" (get-in @store/state [:workspace-data page-id :objects]))))

View file

@ -40,11 +40,6 @@
(throw (ex-info "Invalid arguments" {:v v}))))
([x y] (Point. x y)))
(defn center
[{:keys [x y width height]}]
(point (+ x (/ width 2))
(+ y (/ height 2))))
(defn add
"Returns the addition of the supplied value to both
coordinates of the point as a new point."

View file

@ -376,6 +376,7 @@
maxx (transduce (map :x) max segments)
maxy (transduce (map :y) max segments)]
(assoc shape
:type :rect
:x1 minx
:y1 miny
:x2 maxx
@ -495,7 +496,7 @@
(defn translate-from-frame
[shape {:keys [x y] :as frame}]
(move shape (gpt/point (+ x) (+ y))))
(move shape (gpt/point x y)))
;; --- Alignment
@ -736,13 +737,6 @@
(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 transform-selrect
[frame shape]
(-> shape
(transform-apply-modifiers)
(translate-to-frame frame)
(shape->rect-shape)))
(defn transform-rect-shape
[shape]
(let [;; Apply modifiers to the rect as a path so we have the end shape expected
@ -788,6 +782,8 @@
new-shape (-> shape
(merge rec)
(update :x #(mth/precision % 2))
(update :y #(mth/precision % 2))
(update :transform #(gmt/multiply (or % (gmt/matrix)) stretch-matrix))
(update :transform-inverse #(gmt/multiply stretch-matrix-inverse (or % (gmt/matrix)))))]