mirror of
https://github.com/penpot/penpot.git
synced 2025-02-12 18:18:24 -05:00
🚧 More work on rotation related stuff.
This commit is contained in:
parent
79a91605d3
commit
72d92c419f
9 changed files with 333 additions and 299 deletions
|
@ -354,7 +354,7 @@
|
|||
(ptk/reify ::create-empty-page
|
||||
ptk/WatchEvent
|
||||
(watch [this state stream]
|
||||
(let [file-id (get-in state [:workspace-local :file-id])
|
||||
(let [file-id (get-in state [:workspace-page :file-id])
|
||||
name (str "Page " (gensym "p"))
|
||||
ordering (count (get-in state [:files file-id :pages]))
|
||||
params {:name name
|
||||
|
|
|
@ -582,8 +582,7 @@
|
|||
(update [_ state]
|
||||
(let [shape (-> (geom/setup-proportions data)
|
||||
(assoc :id id))
|
||||
shape (merge canvas-default-attrs shape)
|
||||
shape (recalculate-shape-canvas-relation state shape)]
|
||||
shape (merge canvas-default-attrs shape)]
|
||||
(impl-assoc-shape state shape)))
|
||||
|
||||
ptk/WatchEvent
|
||||
|
@ -592,9 +591,8 @@
|
|||
sid (:session-id state)]
|
||||
(rx/of (commit-changes [{:type :add-canvas
|
||||
:session-id sid
|
||||
:id id
|
||||
:shape shape}])
|
||||
(select-shape id)))))))
|
||||
:shape shape
|
||||
:id id}])))))))
|
||||
|
||||
|
||||
;; --- Duplicate Selected
|
||||
|
@ -653,7 +651,7 @@
|
|||
;; --- Select Shapes (By selrect)
|
||||
|
||||
(defn- impl-try-match-shape
|
||||
[xf selrect acc {:keys [type id items] :as shape}]
|
||||
[selrect acc {:keys [type id items] :as shape}]
|
||||
(cond
|
||||
(geom/contained-in? shape selrect)
|
||||
(conj acc id)
|
||||
|
@ -667,14 +665,16 @@
|
|||
(defn impl-match-by-selrect
|
||||
[state selrect]
|
||||
(let [data (:workspace-data state)
|
||||
match (partial impl-try-match-shape selrect)
|
||||
shapes (:shapes data)
|
||||
xf (comp (map #(get-in data [:shapes-by-id %]))
|
||||
(remove :hidden)
|
||||
(remove :blocked)
|
||||
(remove #(= :canvas (:type %)))
|
||||
(map geom/selection-rect))
|
||||
match (partial impl-try-match-shape xf selrect)
|
||||
shapes (:shapes data)]
|
||||
(reduce match #{} (sequence xf shapes))))
|
||||
(map geom/shape->rect-shape)
|
||||
(map geom/resolve-rotation)
|
||||
(map geom/shape->rect-shape))]
|
||||
(transduce xf match #{} shapes)))
|
||||
|
||||
(def select-shapes-by-current-selrect
|
||||
(ptk/reify ::select-shapes-by-current-selrect
|
||||
|
@ -788,34 +788,35 @@
|
|||
|
||||
(defn impl-dissoc-shape
|
||||
"Given a shape, removes it from the state."
|
||||
[state {:keys [id type] :as shape}]
|
||||
(as-> state $$
|
||||
(if (= :canvas type)
|
||||
(update-in $$ [:workspace-data :canvas]
|
||||
(fn [items] (vec (remove #(= % id) items))))
|
||||
(update-in $$ [:workspace-data :shapes]
|
||||
(fn [items] (vec (remove #(= % id) items)))))
|
||||
(update-in $$ [:workspace-data :shapes-by-id] dissoc id)))
|
||||
[state id]
|
||||
(-> state
|
||||
(update-in [:workspace-data :canvas] (fn [items] (filterv #(not= % id) items)))
|
||||
(update-in [:workspace-data :shapes] (fn [items] (filterv #(not= % id) items)))
|
||||
(update-in [:workspace-data :shapes-by-id] dissoc id)))
|
||||
|
||||
(defn impl-lookup-shape
|
||||
[state id]
|
||||
(get-in state [:workspace-data :shapes-by-id id]))
|
||||
|
||||
(def delete-selected
|
||||
"Deselect all and remove all selected shapes."
|
||||
(ptk/reify ::delete-selected
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [selected (get-in state [:workspace-local :selected])]
|
||||
(reduce impl-dissoc-shape state
|
||||
(map #(get-in state [:workspace-data :shapes-by-id %]) selected))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [selected (get-in state [:workspace-local :selected])
|
||||
session-id (:session-id state)
|
||||
changes (mapv (fn [id]
|
||||
{:type :del-shape
|
||||
:session-id session-id
|
||||
:id id})
|
||||
selected)]
|
||||
(rx/of (commit-changes changes))))))
|
||||
(let [session-id (:session-id state)
|
||||
lookup-shape #(get-in state [:workspace-data :shapes-by-id %])
|
||||
selected (get-in state [:workspace-local :selected])
|
||||
|
||||
changes (->> selected
|
||||
(map lookup-shape)
|
||||
(map (fn [{:keys [type id] :as shape}]
|
||||
{:type (if (= type :canvas) :del-canvas :del-shape)
|
||||
:session-id session-id
|
||||
:id id})))]
|
||||
(rx/merge
|
||||
(rx/of deselect-all)
|
||||
(rx/from (map (fn [id] #(impl-dissoc-shape % id)) selected))
|
||||
(rx/of (commit-changes changes)))))))
|
||||
|
||||
;; --- Rename Shape
|
||||
|
||||
|
@ -998,7 +999,7 @@
|
|||
(ptk/reify ::commit-changes
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [pid (get-in state [:workspace-local :page-id])
|
||||
(let [pid (get-in state [:workspace-page :id])
|
||||
data (get-in state [:pages-data pid])]
|
||||
(update-in state [:pages-data pid] cp/process-changes changes)))
|
||||
|
||||
|
@ -1007,7 +1008,7 @@
|
|||
(let [page (:workspace-page state)
|
||||
params {:id (:id page)
|
||||
:version (:version page)
|
||||
:changes changes}]
|
||||
:changes (vec changes)}]
|
||||
(->> (rp/mutation :update-project-page params)
|
||||
(rx/map shapes-changes-commited))))))
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
:circle (size-circle shape)
|
||||
:curve (size-path shape)
|
||||
:path (size-path shape)
|
||||
(size-rect shape)))
|
||||
shape))
|
||||
|
||||
(defn- size-path
|
||||
[{:keys [segments x1 y1 x2 y2] :as shape}]
|
||||
|
@ -125,13 +125,6 @@
|
|||
:width (- maxx minx)
|
||||
:height (- maxy miny)))))
|
||||
|
||||
(defn- size-rect
|
||||
"A specialized function for calculate size
|
||||
for rect-like shapes."
|
||||
[{:keys [x1 y1 x2 y2] :as shape}]
|
||||
(merge shape {:width (- x2 x1)
|
||||
:height (- y2 y1)}))
|
||||
|
||||
(defn- size-circle
|
||||
"A specialized function for calculate size
|
||||
for circle shape."
|
||||
|
@ -158,7 +151,6 @@
|
|||
|
||||
(defn- assign-proportions-circle
|
||||
[{:as shape}]
|
||||
(prn "assign-proportions-circle" shape)
|
||||
(assoc shape :proportion 1))
|
||||
|
||||
;; TODO: implement the rest of shapes
|
||||
|
@ -265,67 +257,44 @@
|
|||
(case vid
|
||||
:top-left
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x2 shape))
|
||||
(+ (:y2 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x2 shape))
|
||||
(- (:y2 shape))))
|
||||
|
||||
(gmt/scale (gpt/point scalex scaley)
|
||||
(gpt/point (+ (:x2 shape))
|
||||
(+ (:y2 shape)))))
|
||||
:top-right
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x1 shape))
|
||||
(+ (:y2 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x1 shape))
|
||||
(- (:y2 shape))))
|
||||
|
||||
(gmt/scale (gpt/point scalex scaley)
|
||||
(gpt/point (+ (:x1 shape))
|
||||
(+ (:y2 shape)))))
|
||||
:top
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x1 shape))
|
||||
(+ (:y2 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x1 shape))
|
||||
(- (:y2 shape))))
|
||||
|
||||
(gmt/scale (gpt/point scalex scaley)
|
||||
(gpt/point (+ (:x1 shape))
|
||||
(+ (:y2 shape)))))
|
||||
:bottom-left
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x2 shape))
|
||||
(+ (:y1 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x2 shape))
|
||||
(- (:y1 shape))))
|
||||
|
||||
(gmt/scale (gpt/point scalex scaley)
|
||||
(gpt/point (+ (:x2 shape))
|
||||
(+ (:y1 shape)))))
|
||||
:bottom-right
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x shape))
|
||||
(+ (:y shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x shape))
|
||||
(- (:y shape))))
|
||||
|
||||
(gmt/scale (gpt/point scalex scaley)
|
||||
(gpt/point (+ (:x shape))
|
||||
(+ (:y shape)))))
|
||||
:bottom
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x1 shape))
|
||||
(+ (:y1 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x1 shape))
|
||||
(- (:y1 shape))))
|
||||
|
||||
(gmt/scale (gpt/point scalex scaley)
|
||||
(gpt/point (+ (:x1 shape))
|
||||
(+ (:y1 shape)))))
|
||||
:right
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x1 shape))
|
||||
(+ (:y1 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x1 shape))
|
||||
(- (:y1 shape))))
|
||||
|
||||
(gmt/scale (gpt/point scalex scaley)
|
||||
(gpt/point (+ (:x1 shape))
|
||||
(+ (:y1 shape)))))
|
||||
:left
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x2 shape))
|
||||
(+ (:y1 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x2 shape))
|
||||
(- (:y1 shape))))))
|
||||
(gmt/scale (gpt/point scalex scaley)
|
||||
(gpt/point (+ (:x2 shape))
|
||||
(+ (:y1 shape)))))))
|
||||
|
||||
|
||||
(defn resize-shape
|
||||
|
@ -479,28 +448,6 @@
|
|||
:height (- maxy miny)
|
||||
:type :rect}))
|
||||
|
||||
;; (defn shapes->rect-shape'
|
||||
;; [shapes]
|
||||
;; (let [shapes (mapv shape->rect-shape shapes)
|
||||
;; total (count shapes)]
|
||||
;; (loop [idx (int 0)
|
||||
;; minx js/Number.POSITIVE_INFINITY
|
||||
;; miny js/Number.POSITIVE_INFINITY
|
||||
;; maxx js/Number.NEGATIVE_INFINITY
|
||||
;; maxy js/Number.NEGATIVE_INFINITY]
|
||||
;; (if (> total idx)
|
||||
;; (let [{:keys [x1 y1 x2 y2]} (nth shapes idx)]
|
||||
;; (recur (inc idx)
|
||||
;; (min minx x1)
|
||||
;; (min miny y1)
|
||||
;; (max maxx x2)
|
||||
;; (max maxy y2)))
|
||||
;; {:x1 minx
|
||||
;; :y1 miny
|
||||
;; :x2 maxx
|
||||
;; :y2 maxy
|
||||
;; :type :rect}))))
|
||||
|
||||
(defn- rect->rect-shape
|
||||
[{:keys [x y width height] :as shape}]
|
||||
(assoc shape
|
||||
|
@ -511,10 +458,10 @@
|
|||
|
||||
(defn- path->rect-shape
|
||||
[{:keys [segments] :as shape}]
|
||||
(let [minx (apply min (map :x segments))
|
||||
miny (apply min (map :y segments))
|
||||
maxx (apply max (map :x segments))
|
||||
maxy (apply max (map :y segments))]
|
||||
(let [minx (transduce (map :x) min segments)
|
||||
miny (transduce (map :y) min segments)
|
||||
maxx (transduce (map :x) max segments)
|
||||
maxy (transduce (map :y) max segments)]
|
||||
(assoc shape
|
||||
:x1 minx
|
||||
:y1 miny
|
||||
|
@ -566,6 +513,7 @@
|
|||
tr (gpt/transform [(+ x width) y] mx)
|
||||
bl (gpt/transform [x (+ y height)] mx)
|
||||
br (gpt/transform [(+ x width) (+ y height)] mx)
|
||||
;; TODO: replace apply with transduce (performance)
|
||||
minx (apply min (map :x [tl tr bl br]))
|
||||
maxx (apply max (map :x [tl tr bl br]))
|
||||
miny (apply min (map :y [tl tr bl br]))
|
||||
|
@ -576,9 +524,6 @@
|
|||
:width (- maxx minx)
|
||||
:height (- maxy miny))))
|
||||
|
||||
;; :x2 (+ minx (- maxx minx))
|
||||
;; :y2 (+ miny (- maxy miny)))))
|
||||
|
||||
(defn- transform-circle
|
||||
[{:keys [cx cy rx ry] :as shape} xfmt]
|
||||
(let [{:keys [x1 y1 x2 y2]} (shape->rect-shape shape)
|
||||
|
@ -587,6 +532,7 @@
|
|||
bl (gpt/transform [x1 y2] xfmt)
|
||||
br (gpt/transform [x2 y2] xfmt)
|
||||
|
||||
;; TODO: replace apply with transduce (performance)
|
||||
x (apply min (map :x [tl tr bl br]))
|
||||
y (apply min (map :y [tl tr bl br]))
|
||||
maxx (apply max (map :x [tl tr bl br]))
|
||||
|
@ -608,30 +554,48 @@
|
|||
|
||||
(defn rotation-matrix
|
||||
"Generate a rotation matrix from shape."
|
||||
[{:keys [x1 y1 rotation] :as shape}]
|
||||
(let [{:keys [width height]} (size shape)
|
||||
x-center (+ x1 (/ width 2))
|
||||
y-center (+ y1 (/ height 2))]
|
||||
(-> (gmt/matrix)
|
||||
;; (gmt/rotate* rotation (gpt/point x-center y-center)))))
|
||||
(gmt/translate x-center y-center)
|
||||
(gmt/rotate rotation)
|
||||
(gmt/translate (- x-center) (- y-center)))))
|
||||
[{:keys [x y width height rotation] :as shape}]
|
||||
(let [cx (+ x (/ width 2))
|
||||
cy (+ y (/ height 2))]
|
||||
(cond-> (gmt/matrix)
|
||||
(and rotation (pos? rotation))
|
||||
(gmt/rotate rotation (gpt/point cx cy)))))
|
||||
|
||||
(defn rotate-shape
|
||||
"Apply the transformation matrix to the shape."
|
||||
(defn resolve-rotation
|
||||
[shape]
|
||||
(let [mtx (rotation-matrix (size shape))]
|
||||
(transform shape mtx)))
|
||||
(transform shape (rotation-matrix shape)))
|
||||
|
||||
(defn resolve-modifier
|
||||
[{:keys [modifier-mtx] :as shape}]
|
||||
(cond-> shape
|
||||
(gmt/matrix? modifier-mtx)
|
||||
(transform modifier-mtx)))
|
||||
|
||||
(def ^:private
|
||||
xf-resolve-shapes
|
||||
(comp (map shape->rect-shape)
|
||||
(map resolve-modifier)
|
||||
(map resolve-rotation)
|
||||
(map shape->rect-shape)))
|
||||
|
||||
(defn selection-rect
|
||||
"Return the selection rect for the shape."
|
||||
[shape]
|
||||
(let [modifier (:modifier-mtx shape)]
|
||||
(-> (shape->rect-shape shape)
|
||||
(assoc :type :rect :id (:id shape))
|
||||
(transform (or modifier (gmt/matrix)))
|
||||
#_(rotate-shape))))
|
||||
"Returns a rect that contains all the shapes and is aware of the
|
||||
rotation of each shape. Mainly used for multiple selection."
|
||||
[shapes]
|
||||
(let [shapes (into [] xf-resolve-shapes shapes)
|
||||
minx (transduce (map :x1) min shapes)
|
||||
miny (transduce (map :y1) min shapes)
|
||||
maxx (transduce (map :x2) max shapes)
|
||||
maxy (transduce (map :y2) max shapes)]
|
||||
{:x1 minx
|
||||
:y1 miny
|
||||
:x2 maxx
|
||||
:y2 maxy
|
||||
:x minx
|
||||
:y miny
|
||||
:width (- maxx minx)
|
||||
:height (- maxy miny)
|
||||
:type :rect}))
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
|
|
|
@ -32,13 +32,6 @@
|
|||
|
||||
;; --- Rect Shape
|
||||
|
||||
(defn- rotate
|
||||
[mt {:keys [x1 y1 x2 y2 width height rotation] :as shape}]
|
||||
(let [x-center (+ x1 (/ width 2))
|
||||
y-center (+ y1 (/ height 2))
|
||||
center (gpt/point x-center y-center)]
|
||||
(gmt/rotate* mt rotation center)))
|
||||
|
||||
(mf/defc rect-shape
|
||||
[{:keys [shape] :as props}]
|
||||
(let [{:keys [id rotation modifier-mtx]} shape
|
||||
|
@ -49,18 +42,16 @@
|
|||
|
||||
{:keys [x y width height]} shape
|
||||
|
||||
;; transform (when (pos? rotation)
|
||||
;; (str (rotate (gmt/matrix) shape)))
|
||||
|
||||
transform (str/format "rotate(%s %s %s)"
|
||||
rotation
|
||||
(+ x (/ width 2))
|
||||
(+ y (/ height 2)))
|
||||
transform (when (and rotation (pos? rotation))
|
||||
(str/format "rotate(%s %s %s)"
|
||||
rotation
|
||||
(+ x (/ width 2))
|
||||
(+ y (/ height 2))))
|
||||
|
||||
props (-> (attrs/extract-style-attrs shape)
|
||||
(assoc :x x
|
||||
:y y
|
||||
;; :transform transform
|
||||
:transform transform
|
||||
:id (str "shape-" id)
|
||||
:width width
|
||||
:height height
|
||||
|
|
|
@ -304,10 +304,10 @@
|
|||
|
||||
(mf/defc generic-draw-area
|
||||
[{:keys [shape zoom]}]
|
||||
(let [{:keys [x1 y1 width height]} (geom/selection-rect shape)]
|
||||
(let [{:keys [x y width height]} (geom/shape->rect-shape shape)]
|
||||
[:g
|
||||
[:& shapes/shape-wrapper {:shape shape}]
|
||||
[:rect.main {:x x1 :y y1
|
||||
[:rect.main {:x x :y y
|
||||
:width width
|
||||
:height height
|
||||
:stroke-dasharray (str (/ 5.0 zoom) "," (/ 5 zoom))
|
||||
|
|
|
@ -20,9 +20,14 @@
|
|||
[uxbox.main.streams :as ms]
|
||||
[uxbox.main.workers :as uwrk]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.geom.point :as gpt]))
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.util.geom.matrix :as gmt]))
|
||||
|
||||
;; --- Resize Implementation
|
||||
(defn- apply-zoom
|
||||
[point]
|
||||
(gpt/divide point @refs/selected-zoom))
|
||||
|
||||
;; --- Resize & Rotate
|
||||
|
||||
(defn- start-resize
|
||||
[vid ids shape]
|
||||
|
@ -44,11 +49,7 @@
|
|||
(apply-grid-alignment [point]
|
||||
(if @refs/selected-alignment
|
||||
(uwrk/align-point point)
|
||||
(rx/of point)))
|
||||
|
||||
;; Apply the current zoom factor to the point.
|
||||
(apply-zoom [point]
|
||||
(gpt/divide point @refs/selected-zoom))]
|
||||
(rx/of point)))]
|
||||
(reify
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
|
@ -78,11 +79,8 @@
|
|||
|
||||
(rx/concat
|
||||
(->> ms/mouse-position
|
||||
;; (rx/map apply-zoom)
|
||||
;; (rx/mapcat apply-grid-alignment)
|
||||
(rx/map apply-zoom)
|
||||
(rx/with-latest vector ms/mouse-position-ctrl)
|
||||
;; (rx/map normalize-proportion-lock)
|
||||
;; (rx/mapcat (partial resize shape))
|
||||
(rx/map (fn [[pos ctrl?]]
|
||||
(let [angle (+ (gpt/angle pos center) 90)
|
||||
angle (if (neg? angle)
|
||||
|
@ -93,6 +91,9 @@
|
|||
(if (< 50 modval)
|
||||
(+ angle (- 90 modval))
|
||||
(- angle modval))
|
||||
angle)
|
||||
angle (if (= angle 360)
|
||||
0
|
||||
angle)]
|
||||
(dw/update-shape (:id shape) {:rotation angle}))))
|
||||
(rx/take-until stoper)))))))
|
||||
|
@ -121,36 +122,35 @@
|
|||
|
||||
(mf/defc controls
|
||||
[{:keys [shape zoom on-resize on-rotate] :as props}]
|
||||
(let [{:keys [x y width height]} shape
|
||||
(let [{:keys [x y width height rotation]} shape
|
||||
radius (if (> (max width height) handler-size-threshold) 6.0 4.0)
|
||||
|
||||
transform (str/format "rotate(%s %s %s)"
|
||||
(:rotation shape 0)
|
||||
(+ (:x shape) (/ (:width shape) 2))
|
||||
(+ (:y shape) (/ (:height shape) 2)))]
|
||||
|
||||
[:g.controls #_{:transform transform}
|
||||
transform (geom/rotation-matrix shape)]
|
||||
[:g.controls {:transform transform}
|
||||
[:rect.main {:x x :y y
|
||||
:width width
|
||||
:height height
|
||||
:stroke-dasharray (str (/ 8.0 zoom) "," (/ 5 zoom))
|
||||
:style {:stroke "#31EFB8" :fill "transparent"
|
||||
:stroke-opacity "1"}}]
|
||||
[:path {:stroke "#31EFB8"
|
||||
:stroke-opacity "1"
|
||||
:stroke-dasharray (str (/ 8.0 zoom) "," (/ 5 zoom))
|
||||
:fill "transparent"
|
||||
:d (str/format "M %s %s L %s %s"
|
||||
(+ x (/ width 2))
|
||||
y
|
||||
(+ x (/ width 2))
|
||||
(- y 30))}]
|
||||
|
||||
[:& control-item {:class "rotate"
|
||||
:r (/ radius zoom)
|
||||
:cx (+ x (/ width 2))
|
||||
:on-click on-rotate
|
||||
:cy (- y 30)}]
|
||||
(when (fn? on-rotate)
|
||||
[:*
|
||||
[:path {:stroke "#31EFB8"
|
||||
:stroke-opacity "1"
|
||||
:stroke-dasharray (str (/ 8.0 zoom) "," (/ 5 zoom))
|
||||
:fill "transparent"
|
||||
:d (str/format "M %s %s L %s %s"
|
||||
(+ x (/ width 2))
|
||||
y
|
||||
(+ x (/ width 2))
|
||||
(- y 30))}]
|
||||
|
||||
[:& control-item {:class "rotate"
|
||||
:r (/ radius zoom)
|
||||
:cx (+ x (/ width 2))
|
||||
:on-click on-rotate
|
||||
:cy (- y 30)}]])
|
||||
|
||||
[:& control-item {:class "top"
|
||||
:on-click #(on-resize :top %)
|
||||
:r (/ radius zoom)
|
||||
|
@ -232,21 +232,10 @@
|
|||
|
||||
;; TODO: add specs for clarity
|
||||
|
||||
(mf/defc multiple-selection-handlers
|
||||
[{:keys [shapes selected zoom] :as props}]
|
||||
(let [shape (->> shapes
|
||||
(map #(geom/selection-rect %))
|
||||
(geom/shapes->rect-shape)
|
||||
(geom/selection-rect))
|
||||
on-resize #(do (dom/stop-propagation %2)
|
||||
(st/emit! (start-resize %1 selected shape)))]
|
||||
[:& controls {:shape shape
|
||||
:zoom zoom
|
||||
:on-resize on-resize}]))
|
||||
|
||||
(mf/defc text-edition-selection-handlers
|
||||
[{:keys [shape zoom] :as props}]
|
||||
(let [{:keys [x y width height] :as shape} (geom/selection-rect shape)]
|
||||
(let [{:keys [x y width height] :as shape} shape]
|
||||
[:g.controls
|
||||
[:rect.main {:x x :y y
|
||||
:width width
|
||||
|
@ -257,13 +246,25 @@
|
|||
:stroke-opacity "1"
|
||||
:fill "transparent"}}]]))
|
||||
|
||||
(mf/defc multiple-selection-handlers
|
||||
[{:keys [shapes selected zoom] :as props}]
|
||||
(let [shape (geom/selection-rect shapes)
|
||||
on-resize #(do (dom/stop-propagation %2)
|
||||
(st/emit! (start-resize %1 selected shape)))]
|
||||
[:& controls {:shape shape
|
||||
:zoom zoom
|
||||
:on-resize on-resize}]))
|
||||
|
||||
(mf/defc single-selection-handlers
|
||||
[{:keys [shape zoom] :as props}]
|
||||
(prn "single-selection-handlers" shape)
|
||||
(let [on-resize #(do (dom/stop-propagation %2)
|
||||
(st/emit! (start-resize %1 #{(:id shape)} shape)))
|
||||
on-rotate #(do (dom/stop-propagation %)
|
||||
#_(st/emit! (start-rotate shape)))
|
||||
shape (geom/selection-rect shape)]
|
||||
(st/emit! (start-rotate shape)))
|
||||
modifier (:modifier-mtx shape)
|
||||
shape (-> (geom/shape->rect-shape shape)
|
||||
(geom/transform (or modifier (gmt/matrix))))]
|
||||
[:& controls {:shape shape
|
||||
:zoom zoom
|
||||
:on-rotate on-rotate
|
||||
|
@ -272,7 +273,11 @@
|
|||
(mf/defc selection-handlers
|
||||
[{:keys [selected edition zoom] :as props}]
|
||||
(let [data (mf/deref refs/workspace-data)
|
||||
shapes (map #(get-in data [:shapes-by-id %]) selected)
|
||||
;; We need remove posible nil values because on shape
|
||||
;; deletion many shape will reamin selected and deleted
|
||||
;; in the same time for small instant of time
|
||||
shapes (->> (map #(get-in data [:shapes-by-id %]) selected)
|
||||
(remove nil?))
|
||||
num (count shapes)
|
||||
{:keys [id type] :as shape} (first shapes)]
|
||||
(cond
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
;; --- Matrix Impl
|
||||
|
||||
(defrecord Matrix [a b c d tx ty])
|
||||
(defrecord Matrix [a b c d e f])
|
||||
|
||||
(defprotocol ICoerce
|
||||
"Matrix coersion protocol."
|
||||
|
@ -22,7 +22,7 @@
|
|||
(extend-type Matrix
|
||||
cljs.core/IDeref
|
||||
(-deref [v]
|
||||
(mapv #(get v %) [:a :c :b :d :tx :ty]))
|
||||
(mapv #(get v %) [:a :b :c :d :e :f]))
|
||||
|
||||
Object
|
||||
(toString [v]
|
||||
|
@ -39,37 +39,26 @@
|
|||
|
||||
cljs.core/PersistentVector
|
||||
(-matrix [v]
|
||||
(let [[a b c d tx ty] v]
|
||||
(Matrix. a b c d tx ty)))
|
||||
(let [[a b c d e f] v]
|
||||
(Matrix. a b c d e f)))
|
||||
|
||||
cljs.core/IndexedSeq
|
||||
(-matrix [v]
|
||||
(let [[a b c d tx ty] v]
|
||||
(Matrix. a b c d tx ty))))
|
||||
(let [[a b c d e f] v]
|
||||
(Matrix. a b c d e f))))
|
||||
|
||||
(defn multiply
|
||||
([m om]
|
||||
(let [a1 (:a m)
|
||||
b1 (:b m)
|
||||
c1 (:c m)
|
||||
d1 (:d m)
|
||||
a2 (:a om)
|
||||
b2 (:b om)
|
||||
c2 (:c om)
|
||||
d2 (:d om)
|
||||
tx1 (:tx m)
|
||||
ty1 (:ty m)
|
||||
tx2 (:tx om)
|
||||
ty2 (:ty om)]
|
||||
(Matrix.
|
||||
(+ (* a2 a1) (* c2 c1))
|
||||
(+ (* a2 b1) (* c2 d1))
|
||||
(+ (* b2 a1) (* d2 c1))
|
||||
(+ (* b2 b1) (* d2 d1))
|
||||
(+ tx1 (* tx2 a1) (* ty2 c1))
|
||||
(+ ty1 (* tx2 b1) (* ty2 d1)))))
|
||||
([m om & others]
|
||||
(reduce multiply (multiply m om) others)))
|
||||
([m1 m2]
|
||||
(Matrix.
|
||||
(+ (* (:a m1) (:a m2)) (* (:c m1) (:b m2)))
|
||||
(+ (* (:b m1) (:a m2)) (* (:d m1) (:b m2)))
|
||||
(+ (* (:a m1) (:c m2)) (* (:c m1) (:d m2)))
|
||||
(+ (* (:b m1) (:c m2)) (* (:d m1) (:d m2)))
|
||||
(+ (* (:a m1) (:e m2)) (* (:c m1) (:f m2)) (:e m1))
|
||||
(+ (* (:b m1) (:e m2)) (* (:d m1) (:f m2)) (:f m1))))
|
||||
|
||||
([m1 m2 & others]
|
||||
(reduce multiply (multiply m1 m2) others)))
|
||||
|
||||
(defn ^boolean matrix?
|
||||
"Return true if `v` is Matrix instance."
|
||||
|
@ -82,30 +71,98 @@
|
|||
(Matrix. 1 0 0 1 0 0))
|
||||
([v]
|
||||
(-matrix v))
|
||||
([a b c d tx ty]
|
||||
(Matrix. a b c d tx ty)))
|
||||
([a b c d e f]
|
||||
(Matrix. a b c d e f)))
|
||||
|
||||
(defn translate-matrix
|
||||
([pt]
|
||||
(let [pt (gpt/point pt)]
|
||||
(Matrix. 1 0 0 1 (:x pt) (:y pt))))
|
||||
([x y]
|
||||
(translate-matrix (gpt/point x y))))
|
||||
[pt]
|
||||
(let [pt (gpt/point pt)]
|
||||
(Matrix. 1 0 0 1 (:x pt) (:y pt))))
|
||||
|
||||
(defn scale-matrix
|
||||
([s]
|
||||
(Matrix. s 0 0 s 0 0))
|
||||
([sx sy]
|
||||
(Matrix. sx 0 0 sy 0 0)))
|
||||
[s]
|
||||
(let [pt (gpt/point s)]
|
||||
(Matrix. (:x pt) 0 0 (:y pt) 0 0)))
|
||||
|
||||
(defn rotate-matrix
|
||||
[a]
|
||||
(let [a (mth/radians a)]
|
||||
(Matrix. (mth/cos a)
|
||||
(mth/sin a)
|
||||
(- (mth/sin a))
|
||||
(mth/cos a)
|
||||
0 0)))
|
||||
(Matrix.
|
||||
(mth/cos a)
|
||||
(mth/sin a)
|
||||
(- (mth/sin a))
|
||||
(mth/cos a)
|
||||
0
|
||||
0)))
|
||||
|
||||
;; OLD
|
||||
;; (defn rotate
|
||||
;; "Apply rotation transformation to the matrix."
|
||||
;; ([m angle]
|
||||
;; (multiply m (rotate-matrix angle)))
|
||||
;; ([m angle center]
|
||||
;; (multiply m
|
||||
;; (translate-matrix center)
|
||||
;; (rotate-matrix angle)
|
||||
;; (translate-matrix (gpt/negate center)))))
|
||||
|
||||
;; -- ROTATE
|
||||
;; r = radians(r)
|
||||
;; const cos = Math.cos(r)
|
||||
;; const sin = Math.sin(r)
|
||||
;;
|
||||
;; const { a, b, c, d, e, f } = this
|
||||
;;
|
||||
;; this.a = a * cos - b * sin
|
||||
;; this.b = b * cos + a * sin
|
||||
;; this.c = c * cos - d * sin
|
||||
;; this.d = d * cos + c * sin
|
||||
;; this.e = e * cos - f * sin + cy * sin - cx * cos + cx
|
||||
;; this.f = f * cos + e * sin - cx * sin - cy * cos + cy
|
||||
|
||||
;; (defn rotate
|
||||
;; ([m angle] (rotate m angle (gpt/point 0 0)))
|
||||
;; ([m angle center]
|
||||
;; (let [{:keys [a b c d e f]} m
|
||||
;; {cx :x cy :y} center
|
||||
;; r (mth/radians angle)
|
||||
;; cos (mth/cos r)
|
||||
;; sin (mth/sin r)
|
||||
;; a' (- (* a cos) (* b sin))
|
||||
;; b' (+ (* b cos) (* a sin))
|
||||
;; c' (- (* c cos) (* d sin))
|
||||
;; d' (+ (* d cos) (* c sin))
|
||||
;; e' (+ (- (* e cos) (* f sin))
|
||||
;; (- (* cy sin) (* cx cos))
|
||||
;; cx)
|
||||
;; f' (+ (- (+ (* f cos) (* e sin))
|
||||
;; (* cx sin)
|
||||
;; (* cy cos))
|
||||
;; cy)]
|
||||
;; (Matrix. a' b' c' d' e' f'))))
|
||||
|
||||
|
||||
;; export function rotate (angle, cx, cy) {
|
||||
;; const cosAngle = cos(angle)
|
||||
;; const sinAngle = sin(angle)
|
||||
;; const rotationMatrix = {
|
||||
;; a: cosAngle,
|
||||
;; c: -sinAngle,
|
||||
;; e: 0,
|
||||
;; b: sinAngle,
|
||||
;; d: cosAngle,
|
||||
;; f: 0
|
||||
;; }
|
||||
;; if (isUndefined(cx) || isUndefined(cy)) {
|
||||
;; return rotationMatrix
|
||||
;; }
|
||||
|
||||
;; return transform([
|
||||
;; translate(cx, cy),
|
||||
;; rotationMatrix,
|
||||
;; translate(-cx, -cy)
|
||||
;; ])
|
||||
;; }
|
||||
|
||||
(defn rotate
|
||||
"Apply rotation transformation to the matrix."
|
||||
|
@ -113,35 +170,12 @@
|
|||
(multiply m (rotate-matrix angle)))
|
||||
([m angle center]
|
||||
(multiply m
|
||||
(translate-matrix center)
|
||||
(rotate-matrix angle)
|
||||
(translate-matrix (gpt/negate center)))))
|
||||
(translate-matrix center)
|
||||
(rotate-matrix angle)
|
||||
(translate-matrix (gpt/negate center)))))
|
||||
|
||||
(defn rotate*
|
||||
([m angle]
|
||||
(let [center (gpt/point 0 0)]
|
||||
(rotate m angle center)))
|
||||
([m angle center]
|
||||
(let [angle (mth/radians angle)
|
||||
x (:x center)
|
||||
y (:y center)
|
||||
cos (mth/cos angle)
|
||||
sin (mth/sin angle)
|
||||
nsin (- sin)
|
||||
tx (- x (+ (* x cos)) (* y sin))
|
||||
ty (- y (- (* x sin)) (* y cos))
|
||||
a (+ (* cos (:a m)) (* sin (:c m)))
|
||||
b (+ (* cos (:b m)) (* sin (:d m)))
|
||||
c (+ (* nsin (:a m)) (* cos (:c m)))
|
||||
d (+ (* nsin (:b m)) (* cos (:d m)))
|
||||
tx' (+ (:tx m) (* tx (:a m)) (* ty (:c m)))
|
||||
ty' (+ (:ty m) (* tx (:b m)) (* ty (:d m)))]
|
||||
(Matrix. a b c d tx' ty'))))
|
||||
|
||||
(defn scale
|
||||
"Apply scale transformation to the matrix."
|
||||
([m v] (scale m v v))
|
||||
([m vx vy] (multiply m (scale-matrix vx vy))))
|
||||
;; TODO: temporal backward compatibility
|
||||
(def rotate* rotate)
|
||||
|
||||
;; ([m v] (scale m v v))
|
||||
;; ([m x y]
|
||||
|
@ -151,38 +185,77 @@
|
|||
;; :b (* (:b m) y)
|
||||
;; :d (* (:d m) y))))
|
||||
|
||||
|
||||
|
||||
;; scaleO (x, y = x, cx = 0, cy = 0) {
|
||||
;; // Support uniform scaling
|
||||
;; if (arguments.length === 3) {
|
||||
;; cy = cx
|
||||
;; cx = y
|
||||
;; y = x
|
||||
;; }
|
||||
|
||||
;; const { a, b, c, d, e, f } = this
|
||||
|
||||
;; this.a = a * x
|
||||
;; this.b = b * y
|
||||
;; this.c = c * x
|
||||
;; this.d = d * y
|
||||
;; this.e = e * x - cx * x + cx
|
||||
;; this.f = f * y - cy * y + cy
|
||||
|
||||
;; return this
|
||||
;; }
|
||||
|
||||
;; (defn scale
|
||||
;; "Apply scale transformation to the matrix."
|
||||
;; ([m x] (scale m x x))
|
||||
;; ([m x y]
|
||||
;; (let [{:keys [a b c d e f]} m
|
||||
;; cx 0
|
||||
;; cy 0
|
||||
;; a' (* a x)
|
||||
;; b' (* b y)
|
||||
;; c' (* c x)
|
||||
;; d' (* d y)
|
||||
;; e' (+ cx (- (* e x)
|
||||
;; (* cx x)))
|
||||
;; f' (+ cy (- (* f y)
|
||||
;; (* cy y)))]
|
||||
;; (Matrix. a' b' c' d' e f))))
|
||||
|
||||
(defn scale
|
||||
"Apply scale transformation to the matrix."
|
||||
([m s] (multiply m (scale-matrix s)))
|
||||
([m s c]
|
||||
(multiply m
|
||||
(translate-matrix c)
|
||||
(scale-matrix s)
|
||||
(translate-matrix (gpt/negate c)))))
|
||||
|
||||
(defn translate
|
||||
"Apply translate transformation to the matrix."
|
||||
([m pt]
|
||||
(multiply m (translate-matrix pt)))
|
||||
([m x y]
|
||||
(translate m (gpt/point x y))))
|
||||
|
||||
;; ([m pt]
|
||||
;; (let [pt (gpt/point pt)]
|
||||
;; (assoc m
|
||||
;; :tx (+ (:tx m) (* (:x pt) (:a m)) (* (:y pt) (:b m)))
|
||||
;; :ty (+ (:ty m) (* (:x pt) (:c m)) (* (:y pt) (:d m))))))
|
||||
;; ([m x y]
|
||||
;; (translate m (gpt/point x y))))
|
||||
[m pt]
|
||||
(let [pt (gpt/point pt)]
|
||||
(multiply m (translate-matrix pt))))
|
||||
|
||||
(defn ^boolean invertible?
|
||||
[{:keys [a b c d tx ty] :as m}]
|
||||
[{:keys [a b c d e f] :as m}]
|
||||
(let [det (- (* a d) (* c b))]
|
||||
(and (not (mth/nan? det))
|
||||
(mth/finite? tx)
|
||||
(mth/finite? ty))))
|
||||
(mth/finite? e)
|
||||
(mth/finite? f))))
|
||||
|
||||
(defn invert
|
||||
[{:keys [a b c d tx ty] :as m}]
|
||||
[{:keys [a b c d e f] :as m}]
|
||||
(when (invertible? m)
|
||||
(let [det (- (* a d) (* c b))]
|
||||
(Matrix. (/ d det)
|
||||
(/ (- b) det)
|
||||
(/ (- c) det)
|
||||
(/ a det)
|
||||
(/ (- (* c ty) (* d tx)) det)
|
||||
(/ (- (* b tx) (* a ty)) det)))))
|
||||
(/ (- (* c f) (* d e)) det)
|
||||
(/ (- (* b e) (* a f)) det)))))
|
||||
|
||||
;; --- Transit Adapter
|
||||
|
||||
|
|
|
@ -116,7 +116,6 @@
|
|||
If the second vector is not provided, the angle
|
||||
will be measured from x-axis."
|
||||
([p]
|
||||
|
||||
(-> (mth/atan2 (:y p) (:x p))
|
||||
(mth/degrees)))
|
||||
([p center]
|
||||
|
@ -162,10 +161,10 @@
|
|||
|
||||
(defn transform
|
||||
"Transform a point applying a matrix transfomation."
|
||||
[pt {:keys [a b c d tx ty] :as m}]
|
||||
[pt {:keys [a b c d e f] :as m}]
|
||||
(let [{:keys [x y]} (point pt)]
|
||||
(Point. (+ (* x a) (* y c) tx)
|
||||
(+ (* x b) (* y d) ty))))
|
||||
(Point. (+ (* x a) (* y c) e)
|
||||
(+ (* x b) (* y d) f))))
|
||||
|
||||
|
||||
;; --- Transit Adapter
|
||||
|
|
|
@ -201,6 +201,7 @@
|
|||
:pprint-config false
|
||||
:load-warninged-code false
|
||||
:auto-testing false
|
||||
:reload-dependents true
|
||||
:reload-clj-files true
|
||||
:css-dirs ["resources/public/css"]
|
||||
:ring-server-options {:port 3449 :host "0.0.0.0"}
|
||||
|
|
Loading…
Add table
Reference in a new issue