mirror of
https://github.com/penpot/penpot.git
synced 2025-02-10 00:58:26 -05:00
Normalize coordinates on shapes.
This commit is contained in:
parent
6f2a22062b
commit
6d89c573a6
5 changed files with 220 additions and 185 deletions
|
@ -194,13 +194,14 @@
|
|||
instread final point of coordinates.
|
||||
|
||||
WARN: only works with shapes that works
|
||||
with height and width such are"
|
||||
with height and width such are ::rect"
|
||||
[sid {:keys [width height] :as opts}]
|
||||
(sc/validate! +shape-size-schema+ opts)
|
||||
(reify
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(let [size {:width width :height height}]
|
||||
(let [shape (get-in state [:shapes-by-id sid])
|
||||
size (merge (sh/-size shape) opts)]
|
||||
(update-in state [:shapes-by-id sid] sh/-resize' size)))))
|
||||
|
||||
(defn update-position
|
||||
|
@ -210,7 +211,7 @@
|
|||
(reify
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(update-in state [:shapes-by-id sid] sh/-move' [x y]))))
|
||||
(update-in state [:shapes-by-id sid] sh/-move' opts))))
|
||||
|
||||
(defn update-fill-attrs
|
||||
[sid {:keys [color opacity] :as opts}]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
(ns uxbox.shapes
|
||||
(:require [uxbox.util.matrix :as mtx]
|
||||
(:require [uxbox.util.geom.matrix :as gmt]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.util.math :as mth]
|
||||
[uxbox.state :as st]))
|
||||
|
||||
|
@ -54,6 +55,10 @@
|
|||
dispatch-by-type
|
||||
:hierarchy #'+hierarchy+)
|
||||
|
||||
(defmulti -size
|
||||
dispatch-by-type
|
||||
:hierarchy #'+hierarchy+)
|
||||
|
||||
(defmulti -rotate
|
||||
dispatch-by-type
|
||||
:hierarchy #'+hierarchy+)
|
||||
|
@ -75,6 +80,10 @@
|
|||
dispatch-by-type
|
||||
:hierarchy #'+hierarchy+)
|
||||
|
||||
(defmulti -transformation
|
||||
dispatch-by-type
|
||||
:hierarchy #'+hierarchy+)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Implementation
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -84,10 +93,12 @@
|
|||
(defmethod -initialize ::shape
|
||||
[shape {:keys [x1 y1 x2 y2]}]
|
||||
(assoc shape
|
||||
:x x1
|
||||
:y y1
|
||||
:width (- x2 x1)
|
||||
:height (- y2 y1)))
|
||||
:x1 x1
|
||||
:y1 y1
|
||||
:x2 x2
|
||||
:y2 y2))
|
||||
;; :width (- x2 x1)
|
||||
;; :height (- y2 y1)))
|
||||
|
||||
(defmethod -initialize :builtin/group
|
||||
[shape {:keys [x1 y1 x2 y2] :as props}]
|
||||
|
@ -103,53 +114,34 @@
|
|||
|
||||
(defmethod -initialize :builtin/circle
|
||||
[shape {:keys [x1 y1 x2 y2]}]
|
||||
(let [width (- x2 x1)
|
||||
height (- y2 y1)
|
||||
(assoc shape
|
||||
:cx x1
|
||||
:cy y1
|
||||
:rx (mth/abs (- x2 x1))
|
||||
:ry (mth/abs (- y2 y1))))
|
||||
|
||||
rx (/ width 2)
|
||||
ry (/ height 2)
|
||||
|
||||
cx (+ x1 (/ width 2))
|
||||
cy (+ y1 (/ height 2))]
|
||||
(assoc shape :rx rx :ry ry :cx cx :cy cy)))
|
||||
|
||||
;; Resize
|
||||
;; FIXME: lock mode
|
||||
|
||||
(defmethod -resize :builtin/line
|
||||
[shape {:keys [x y] :as pos}]
|
||||
(assoc shape
|
||||
:x2 x :y2 x))
|
||||
(assoc shape :x2 x :y2 y))
|
||||
|
||||
(defmethod -resize :builtin/circle
|
||||
[{:keys [cx cy rx ry] :as shape} {:keys [x y lock] :as pos}]
|
||||
(let [x1 (- cx rx)
|
||||
y1 (- cy ry)
|
||||
x2 x
|
||||
y2 y
|
||||
[shape {:keys [x y lock] :as pos}]
|
||||
(let [cx (:cx shape)
|
||||
cy (:cy shape)
|
||||
|
||||
width (- x2 x1)
|
||||
height (if lock
|
||||
width
|
||||
(- y2 y1))
|
||||
|
||||
rx (/ width 2)
|
||||
ry (/ height 2)
|
||||
|
||||
cx (+ x1 (/ width 2))
|
||||
cy (+ y1 (/ height 2))]
|
||||
rx (mth/abs (- x cx))
|
||||
ry (mth/abs (- y cy))]
|
||||
(if lock
|
||||
(assoc shape :rx rx :ry ry :cx cx :cy cy)
|
||||
(assoc shape :rx rx :ry ry :cx cx :cy cy))))
|
||||
(assoc shape :rx rx :ry rx)
|
||||
(assoc shape :rx rx :ry ry))))
|
||||
|
||||
(defmethod -resize :builtin/rect
|
||||
[shape {:keys [x y lock] :as pos}]
|
||||
(if lock
|
||||
(assoc shape
|
||||
:width (- x (:x shape))
|
||||
:height (- x (:x shape)))
|
||||
(assoc shape
|
||||
:width (- x (:x shape))
|
||||
:height (- y (:y shape)))))
|
||||
(assoc shape :x2 x :y2 x)
|
||||
(assoc shape :x2 x :y2 y)))
|
||||
|
||||
(defmethod -resize :default
|
||||
[shape _]
|
||||
|
@ -157,21 +149,38 @@
|
|||
|
||||
(defmethod -resize' ::rect
|
||||
[shape {:keys [width height] :as size}]
|
||||
(merge shape
|
||||
(when width {:width width})
|
||||
(when height {:height height})))
|
||||
(let [x1 (:x1 shape)
|
||||
y1 (:y1 shape)]
|
||||
(assoc shape
|
||||
:x2 (+ x1 width)
|
||||
:y2 (+ y1 height))))
|
||||
|
||||
;; (merge shape
|
||||
;; (when width {:width width})
|
||||
;; (when height {:height height})))
|
||||
|
||||
(defmethod -resize' :default
|
||||
[shape _]
|
||||
(throw (ex-info "Not implemented" (select-keys shape [:type]))))
|
||||
|
||||
(defmethod -size ::rect
|
||||
[{:keys [x1 y1 x2 y2] :as shape}]
|
||||
{:width (- x2 x1)
|
||||
:height (- y2 y1)})
|
||||
|
||||
(defmethod -size :default
|
||||
[shape _]
|
||||
(throw (ex-info "Not implemented" (select-keys shape [:type]))))
|
||||
|
||||
;; Move
|
||||
|
||||
(defmethod -move ::rect
|
||||
[shape {dx :x dy :y}]
|
||||
(assoc shape
|
||||
:x (+ (:x shape) dx)
|
||||
:y (+ (:y shape) dy)))
|
||||
:x1 (+ (:x1 shape) dx)
|
||||
:y1 (+ (:y1 shape) dy)
|
||||
:x2 (+ (:x2 shape) dx)
|
||||
:y2 (+ (:y2 shape) dy)))
|
||||
|
||||
(defmethod -move :builtin/group
|
||||
[shape {dx :x dy :y}]
|
||||
|
@ -198,22 +207,22 @@
|
|||
(throw (ex-info "Not implemented" (select-keys shape [:type]))))
|
||||
|
||||
(defmethod -move' ::rect
|
||||
[shape [x y]]
|
||||
(let [dx (if x (- (:x shape) x) 0)
|
||||
dy (if y (- (:y shape) y) 0)]
|
||||
(-move shape [dx dy])))
|
||||
|
||||
(defmethod -move' :builtin/line
|
||||
[shape [x y]]
|
||||
[shape {:keys [x y] :as pos}]
|
||||
(let [dx (if x (- (:x1 shape) x) 0)
|
||||
dy (if y (- (:y1 shape) y) 0)]
|
||||
(-move shape [dx dy])))
|
||||
(-move shape (gpt/point dx dy))))
|
||||
|
||||
(defmethod -move' :builtin/line
|
||||
[shape {:keys [x y] :as pos}]
|
||||
(let [dx (if x (- (:x1 shape) x) 0)
|
||||
dy (if y (- (:y1 shape) y) 0)]
|
||||
(-move shape (gpt/point dx dy))))
|
||||
|
||||
(defmethod -move' :builtin/circle
|
||||
[shape [x y]]
|
||||
[shape {:keys [x y] :as pos}]
|
||||
(let [dx (if x (- (:cx shape) x) 0)
|
||||
dy (if y (- (:cy shape) y) 0)]
|
||||
(-move shape [dx dy])))
|
||||
(-move shape (gpt/point dx dy))))
|
||||
|
||||
(defmethod -move' :default
|
||||
[shape _]
|
||||
|
@ -229,8 +238,9 @@
|
|||
[{:keys [group] :as shape}]
|
||||
(let [group (get-in @st/state [:shapes-by-id group])]
|
||||
(as-> shape $
|
||||
(assoc $ :x (+ (:x shape) (:dx group 0)))
|
||||
(assoc $ :y (+ (:y shape) (:dy group 0)))
|
||||
(assoc $ :x (+ (:x1 shape) (:dx group 0)))
|
||||
(assoc $ :y (+ (:y1 shape) (:dy group 0)))
|
||||
(merge $ (-size $))
|
||||
(container-rect $))))
|
||||
|
||||
(defmethod -outer-rect :builtin/line
|
||||
|
@ -277,6 +287,26 @@
|
|||
[shape _]
|
||||
(throw (ex-info "Not implemented" (select-keys shape [:type]))))
|
||||
|
||||
(defmethod -transformation :builtin/icon
|
||||
[{:keys [x1 y1 rotation view-box] :or {rotation 0} :as shape}]
|
||||
(let [{:keys [width height]} (-size shape)
|
||||
orig-width (nth view-box 2)
|
||||
orig-height (nth view-box 3)
|
||||
scale-x (/ width orig-width)
|
||||
scale-y (/ height orig-height)
|
||||
center-x (- width (/ width 2))
|
||||
center-y (- height (/ height 2))]
|
||||
(as-> (gmt/matrix) $
|
||||
(gmt/translate $ x1 y1)
|
||||
(gmt/translate $ center-x center-y)
|
||||
(gmt/rotate $ rotation)
|
||||
(gmt/translate $ (- center-x) (- center-y))
|
||||
(gmt/scale $ scale-x scale-y))))
|
||||
|
||||
(defmethod -transformation :default
|
||||
[shape _]
|
||||
(throw (ex-info "Not implemented" (select-keys shape [:type]))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Helpers
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -51,8 +51,8 @@
|
|||
(defmethod sh/-render :builtin/icon
|
||||
[{:keys [data id] :as shape} _]
|
||||
(let [key (str id)
|
||||
rfm (svg/calculate-transform shape)
|
||||
attrs (merge {:id key :key key :transform rfm}
|
||||
rfm (sh/-transformation shape)
|
||||
attrs (merge {:id key :key key :transform (str rfm)}
|
||||
(extract-style-attrs shape)
|
||||
(make-debug-attrs shape))]
|
||||
(html
|
||||
|
|
|
@ -43,10 +43,12 @@
|
|||
|
||||
(define-once :drawing-subscriptions
|
||||
(letfn [(init-shape [shape]
|
||||
(let [{:keys [x y]} (gpt/subtract @wb/mouse-position @wb/scroll)
|
||||
(let [{:keys [x y] :as point} (gpt/subtract @wb/mouse-position
|
||||
@wb/scroll)
|
||||
shape (sh/-initialize shape {:x1 x :y1 y :x2 x :y2 y})]
|
||||
|
||||
(reset! +drawing-shape+ shape)
|
||||
(reset! +drawing-position+ {:x2 x :y2 y :lock false})
|
||||
(reset! +drawing-position+ (assoc point :lock false))
|
||||
|
||||
(as-> wb/interactions-b $
|
||||
(rx/filter #(not= % :shape/movement) $)
|
||||
|
@ -56,8 +58,8 @@
|
|||
(rx/subscribe $ on-value nil on-complete))))
|
||||
|
||||
(on-value [[pos ctrl?]]
|
||||
(let [{:keys [x y] :as pos} (gpt/subtract pos @wb/scroll)]
|
||||
(reset! +drawing-position+ {:x2 x :y2 y :lock ctrl?})))
|
||||
(let [point (gpt/subtract pos @wb/scroll)]
|
||||
(reset! +drawing-position+ (assoc point :lock ctrl?))))
|
||||
|
||||
(on-complete []
|
||||
(let [shape @+drawing-shape+
|
||||
|
|
|
@ -196,76 +196,77 @@
|
|||
sid (:id shape)
|
||||
props {attr value}]
|
||||
(rs/emit! (dw/update-radius-attrs sid props))))]
|
||||
(html
|
||||
[:div.element-set {:key (str (:id menu))}
|
||||
[:div.element-set-title (:name menu)]
|
||||
[:div.element-set-content
|
||||
;; SLIDEBAR FOR ROTATION AND OPACITY
|
||||
[:span "Size"]
|
||||
[:div.row-flex
|
||||
[:input#width.input-text
|
||||
{:placeholder "Width"
|
||||
:type "number"
|
||||
:min "0"
|
||||
:value (:width shape)
|
||||
:on-change (partial on-size-change :width)}]
|
||||
[:div.lock-size i/lock]
|
||||
[:input#width.input-text
|
||||
{:placeholder "Height"
|
||||
:type "number"
|
||||
:min "0"
|
||||
:value (:height shape)
|
||||
:on-change (partial on-size-change :height)}]]
|
||||
(let [size (sh/-size shape)]
|
||||
(html
|
||||
[:div.element-set {:key (str (:id menu))}
|
||||
[:div.element-set-title (:name menu)]
|
||||
[:div.element-set-content
|
||||
;; SLIDEBAR FOR ROTATION AND OPACITY
|
||||
[:span "Size"]
|
||||
[:div.row-flex
|
||||
[:input#width.input-text
|
||||
{:placeholder "Width"
|
||||
:type "number"
|
||||
:min "0"
|
||||
:value (:width size)
|
||||
:on-change (partial on-size-change :width)}]
|
||||
[:div.lock-size i/lock]
|
||||
[:input#width.input-text
|
||||
{:placeholder "Height"
|
||||
:type "number"
|
||||
:min "0"
|
||||
:value (:height size)
|
||||
:on-change (partial on-size-change :height)}]]
|
||||
|
||||
[:span "Position"]
|
||||
[:div.row-flex
|
||||
[:input#width.input-text
|
||||
{:placeholder "x"
|
||||
:type "number"
|
||||
:value (:x shape "")
|
||||
:on-change (partial on-pos-change :x)}]
|
||||
[:input#width.input-text
|
||||
{:placeholder "y"
|
||||
:type "number"
|
||||
:value (:y shape "")
|
||||
:on-change (partial on-pos-change :y)}]]
|
||||
[:span "Position"]
|
||||
[:div.row-flex
|
||||
[:input#width.input-text
|
||||
{:placeholder "x"
|
||||
:type "number"
|
||||
:value (:x1 shape "")
|
||||
:on-change (partial on-pos-change :x)}]
|
||||
[:input#width.input-text
|
||||
{:placeholder "y"
|
||||
:type "number"
|
||||
:value (:y1 shape "")
|
||||
:on-change (partial on-pos-change :y)}]]
|
||||
|
||||
[:span "Border radius"]
|
||||
[:div.row-flex
|
||||
[:input#width.input-text
|
||||
{:placeholder "rx"
|
||||
:type "number"
|
||||
:value (:rx shape "")
|
||||
:on-change (partial on-border-change :rx)}]
|
||||
[:div.lock-size i/lock]
|
||||
[:input#width.input-text
|
||||
{:placeholder "ry"
|
||||
:type "number"
|
||||
:value (:ry shape "")
|
||||
:on-change (partial on-border-change :ry)}]]
|
||||
[:span "Border radius"]
|
||||
[:div.row-flex
|
||||
[:input#width.input-text
|
||||
{:placeholder "rx"
|
||||
:type "number"
|
||||
:value (:rx shape "")
|
||||
:on-change (partial on-border-change :rx)}]
|
||||
[:div.lock-size i/lock]
|
||||
[:input#width.input-text
|
||||
{:placeholder "ry"
|
||||
:type "number"
|
||||
:value (:ry shape "")
|
||||
:on-change (partial on-border-change :ry)}]]
|
||||
|
||||
[:span "Rotation"]
|
||||
[:div.row-flex
|
||||
[:input.slidebar
|
||||
{:type "range"
|
||||
:min 0
|
||||
:max 360
|
||||
:value (:rotation shape 0)
|
||||
:on-change on-rotation-change}]]
|
||||
[:span "Rotation"]
|
||||
[:div.row-flex
|
||||
[:input.slidebar
|
||||
{:type "range"
|
||||
:min 0
|
||||
:max 360
|
||||
:value (:rotation shape 0)
|
||||
:on-change on-rotation-change}]]
|
||||
|
||||
[:div.row-flex
|
||||
[:input#width.input-text
|
||||
{:placeholder ""
|
||||
:type "number"
|
||||
:min 0
|
||||
:max 360
|
||||
:value (:rotation shape "0")
|
||||
:on-change on-rotation-change
|
||||
}]
|
||||
[:input.input-text
|
||||
{:style {:visibility "hidden"}}]
|
||||
]]]
|
||||
)))
|
||||
[:div.row-flex
|
||||
[:input#width.input-text
|
||||
{:placeholder ""
|
||||
:type "number"
|
||||
:min 0
|
||||
:max 360
|
||||
:value (:rotation shape "0")
|
||||
:on-change on-rotation-change
|
||||
}]
|
||||
[:input.input-text
|
||||
{:style {:visibility "hidden"}}]
|
||||
]]]
|
||||
))))
|
||||
|
||||
|
||||
(defmethod -render-menu :menu/icon-measures
|
||||
|
@ -287,62 +288,63 @@
|
|||
sid (:id shape)
|
||||
props {attr value}]
|
||||
(rs/emit! (dw/update-position sid props))))]
|
||||
(html
|
||||
[:div.element-set {:key (str (:id menu))}
|
||||
[:div.element-set-title (:name menu)]
|
||||
[:div.element-set-content
|
||||
;; SLIDEBAR FOR ROTATION AND OPACITY
|
||||
[:span "Size"]
|
||||
[:div.row-flex
|
||||
[:input#width.input-text
|
||||
{:placeholder "Width"
|
||||
:type "number"
|
||||
:min "0"
|
||||
:value (:width shape)
|
||||
:on-change (partial on-size-change :width)}]
|
||||
[:div.lock-size i/lock]
|
||||
[:input#width.input-text
|
||||
{:placeholder "Height"
|
||||
:type "number"
|
||||
:min "0"
|
||||
:value (:height shape)
|
||||
:on-change (partial on-size-change :height)}]]
|
||||
(let [size (sh/-size shape)]
|
||||
(html
|
||||
[:div.element-set {:key (str (:id menu))}
|
||||
[:div.element-set-title (:name menu)]
|
||||
[:div.element-set-content
|
||||
;; SLIDEBAR FOR ROTATION AND OPACITY
|
||||
[:span "Size"]
|
||||
[:div.row-flex
|
||||
[:input#width.input-text
|
||||
{:placeholder "Width"
|
||||
:type "number"
|
||||
:min "0"
|
||||
:value (:width size)
|
||||
:on-change (partial on-size-change :width)}]
|
||||
[:div.lock-size i/lock]
|
||||
[:input#width.input-text
|
||||
{:placeholder "Height"
|
||||
:type "number"
|
||||
:min "0"
|
||||
:value (:height size)
|
||||
:on-change (partial on-size-change :height)}]]
|
||||
|
||||
[:span "Position"]
|
||||
[:div.row-flex
|
||||
[:input#width.input-text
|
||||
{:placeholder "x"
|
||||
:type "number"
|
||||
:value (:x shape "")
|
||||
:on-change (partial on-pos-change :x)}]
|
||||
[:input#width.input-text
|
||||
{:placeholder "y"
|
||||
:type "number"
|
||||
:value (:y shape "")
|
||||
:on-change (partial on-pos-change :y)}]]
|
||||
[:span "Position"]
|
||||
[:div.row-flex
|
||||
[:input#width.input-text
|
||||
{:placeholder "x"
|
||||
:type "number"
|
||||
:value (:x1 shape "")
|
||||
:on-change (partial on-pos-change :x)}]
|
||||
[:input#width.input-text
|
||||
{:placeholder "y"
|
||||
:type "number"
|
||||
:value (:y1 shape "")
|
||||
:on-change (partial on-pos-change :y)}]]
|
||||
|
||||
[:span "Rotation"]
|
||||
[:div.row-flex
|
||||
[:input.slidebar
|
||||
{:type "range"
|
||||
:min 0
|
||||
:max 360
|
||||
:value (:rotation shape 0)
|
||||
:on-change on-rotation-change}]]
|
||||
[:span "Rotation"]
|
||||
[:div.row-flex
|
||||
[:input.slidebar
|
||||
{:type "range"
|
||||
:min 0
|
||||
:max 360
|
||||
:value (:rotation shape 0)
|
||||
:on-change on-rotation-change}]]
|
||||
|
||||
[:div.row-flex
|
||||
[:input#width.input-text
|
||||
{:placeholder ""
|
||||
:type "number"
|
||||
:min 0
|
||||
:max 360
|
||||
:value (:rotation shape "0")
|
||||
:on-change on-rotation-change
|
||||
}]
|
||||
[:input.input-text
|
||||
{:style {:visibility "hidden"}}]
|
||||
]]]
|
||||
)))
|
||||
[:div.row-flex
|
||||
[:input#width.input-text
|
||||
{:placeholder ""
|
||||
:type "number"
|
||||
:min 0
|
||||
:max 360
|
||||
:value (:rotation shape "0")
|
||||
:on-change on-rotation-change
|
||||
}]
|
||||
[:input.input-text
|
||||
{:style {:visibility "hidden"}}]
|
||||
]]]
|
||||
))))
|
||||
|
||||
(defmethod -render-menu :menu/circle-measures
|
||||
[menu own shape]
|
||||
|
|
Loading…
Add table
Reference in a new issue