0
Fork 0
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:
Andrey Antukh 2016-02-06 17:39:37 +02:00
parent 6f2a22062b
commit 6d89c573a6
5 changed files with 220 additions and 185 deletions

View file

@ -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}]

View file

@ -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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -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

View file

@ -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+

View file

@ -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]