mirror of
https://github.com/penpot/penpot.git
synced 2025-03-18 10:41:29 -05:00
Fix many inconsistencies between drawing and resizing.
This enables sharing code between the drawing logic and the simple resizing. Allowing drawing in any direction, not only from top-left to bottom-right. Fixes issue #44.
This commit is contained in:
parent
b7253b7fd5
commit
f82ddac72d
16 changed files with 291 additions and 321 deletions
|
@ -220,7 +220,7 @@
|
|||
(defn apply-temporal-resize
|
||||
"Attach temporal resize transformation to the shape."
|
||||
[id xfmt]
|
||||
{:pre [(gmt/matrix? xfmt)]}
|
||||
{:pre [(gmt/matrix? xfmt) (uuid? id)]}
|
||||
(ApplyTemporalResize. id xfmt))
|
||||
|
||||
;; --- Apply Resize Matrix
|
||||
|
|
|
@ -421,7 +421,6 @@
|
|||
(defn materialize-xfmt
|
||||
[state id xfmt]
|
||||
(let [{:keys [type items] :as shape} (get-in state [:shapes id])]
|
||||
(if (= type :group)
|
||||
(-> (reduce #(materialize-xfmt %1 %2 xfmt) state items)
|
||||
(update-in [:shapes id] geom/transform xfmt))
|
||||
(if (= type :group)
|
||||
(reduce #(materialize-xfmt %1 %2 xfmt) state items)
|
||||
(update-in state [:shapes id] geom/transform xfmt))))
|
||||
|
|
|
@ -166,7 +166,6 @@
|
|||
(:id)))
|
||||
(on-fetched [event]
|
||||
(let [coll (get-first-with-icons @event)]
|
||||
(println "first" coll)
|
||||
(select-icons-toolbox-collection coll)))]
|
||||
(rx/merge
|
||||
(rx/of (udi/fetch-collections)
|
||||
|
|
|
@ -42,6 +42,18 @@
|
|||
[shape]
|
||||
(SelectForDrawing. shape))
|
||||
|
||||
|
||||
;; --- Clear Drawing State
|
||||
|
||||
(deftype ClearDrawingState []
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace dissoc :drawing-tool :drawing)))
|
||||
|
||||
(defn clear-drawing-state
|
||||
[]
|
||||
(ClearDrawingState.))
|
||||
|
||||
;; -- Start Drawing
|
||||
|
||||
(declare on-init-draw)
|
||||
|
@ -80,8 +92,8 @@
|
|||
(let [shape (get-in state [:workspace :drawing])
|
||||
shape (geom/setup shape {:x1 (:x point)
|
||||
:y1 (:y point)
|
||||
:x2 (:x point)
|
||||
:y2 (:y point)})]
|
||||
:x2 (inc (:x point))
|
||||
:y2 (inc (:y point))})]
|
||||
(assoc-in state [:workspace :drawing] shape))))
|
||||
|
||||
(defn initialize-drawing
|
||||
|
@ -94,7 +106,13 @@
|
|||
(deftype UpdateDrawing [position]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:workspace :drawing] geom/resize position)))
|
||||
(let [{:keys [id] :as shape} (-> (get-in state [:workspace :drawing])
|
||||
(geom/shape->rect-shape)
|
||||
(geom/size))
|
||||
result (geom/resize-shape :bottom-right shape position false)
|
||||
scale (geom/calculate-scale-ratio shape result)
|
||||
resize-mtx (geom/generate-resize-matrix :bottom-right shape scale)]
|
||||
(assoc-in state [:workspace :modifiers id] {:resize resize-mtx}))))
|
||||
|
||||
(defn update-drawing
|
||||
[position]
|
||||
|
@ -106,12 +124,14 @@
|
|||
(deftype FinishDrawing []
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(if-let [shape (get-in state [:workspace :drawing])]
|
||||
(rx/of #(update % :workspace dissoc :drawing :drawing-tool)
|
||||
(uds/add-shape shape)
|
||||
(uds/select-first-shape)
|
||||
::uev/interrupt)
|
||||
(rx/empty))))
|
||||
(let [{:keys [id] :as shape} (get-in state [:workspace :drawing])
|
||||
resize-mtx (get-in state [:workspace :modifiers id :resize])]
|
||||
(if-not shape
|
||||
(rx/empty)
|
||||
(rx/of (clear-drawing-state)
|
||||
(uds/add-shape (geom/transform shape resize-mtx))
|
||||
(uds/select-first-shape)
|
||||
::uev/interrupt)))))
|
||||
|
||||
(defn finish-drawing
|
||||
[]
|
||||
|
|
|
@ -246,73 +246,162 @@
|
|||
(assoc :ry ry)
|
||||
(assoc :rx (* ry proportion))))))
|
||||
|
||||
;; --- Resize (Absolute)
|
||||
;; --- Resize
|
||||
|
||||
(declare resize-rect)
|
||||
(declare resize-circle)
|
||||
(declare normalize-shape)
|
||||
(declare equalize-sides)
|
||||
(defn calculate-scale-ratio
|
||||
"Calculate the scale factor from one shape to an other.
|
||||
|
||||
(defn resize
|
||||
"Resize the shape using absolute position.
|
||||
NOTE: used in draw operation."
|
||||
[shape point]
|
||||
(case (:type shape)
|
||||
:rect (resize-rect shape point)
|
||||
:icon (resize-rect shape point)
|
||||
:image (resize-rect shape point)
|
||||
:text (resize-rect shape point)
|
||||
:path (resize-rect shape point)
|
||||
:circle (resize-circle shape point)))
|
||||
The shapes should be of rect-like type because width
|
||||
and height are used for calculate the ratio."
|
||||
[origin final]
|
||||
[(/ (:width final) (:width origin))
|
||||
(/ (:height final) (:height origin))])
|
||||
|
||||
(defn- resize-rect
|
||||
"A specialized function for absolute resize
|
||||
for rect-like shapes."
|
||||
[shape {:keys [x y] :as pos}]
|
||||
(-> (assoc shape :x2 x :y2 y)
|
||||
(normalize-shape)))
|
||||
(defn generate-resize-matrix
|
||||
"Generate the resize transformation matrix given a corner-id, shape
|
||||
and the scale factor vector. The shape should be of rect-like type.
|
||||
|
||||
(defn- resize-circle
|
||||
"A specialized function for absolute resize
|
||||
for circle shapes."
|
||||
[shape {:keys [x y lock] :as pos}]
|
||||
(let [cx (:cx shape)
|
||||
cy (:cy shape)
|
||||
Mainly used by drawarea and shape resize on workspace."
|
||||
[vid shape [scalex scaley]]
|
||||
(case vid
|
||||
:top-left
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x2 shape))
|
||||
(+ (:y2 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x2 shape))
|
||||
(- (:y2 shape))))
|
||||
|
||||
rx (mth/abs (- x cx))
|
||||
ry (mth/abs (- y cy))]
|
||||
(if lock
|
||||
(assoc shape :rx rx :ry rx)
|
||||
(assoc shape :rx rx :ry ry))))
|
||||
:top-right
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x1 shape))
|
||||
(+ (:y2 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x1 shape))
|
||||
(- (:y2 shape))))
|
||||
|
||||
(defn- normalize-shape
|
||||
"Normalize shape coordinates."
|
||||
[shape]
|
||||
(let [x1 (min (:x1 shape) (:x2 shape))
|
||||
y1 (min (:y1 shape) (:y2 shape))
|
||||
x2 (max (:x1 shape) (:x2 shape))
|
||||
y2 (max (:y1 shape) (:y2 shape))]
|
||||
(assoc shape :x1 x1 :x2 x2 :y1 y1 :y2 y2)))
|
||||
:top
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x1 shape))
|
||||
(+ (:y2 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x1 shape))
|
||||
(- (:y2 shape))))
|
||||
|
||||
(defn- equalize-sides
|
||||
"Fix shape sides to be equal according to the lock mode."
|
||||
[shape]
|
||||
(let [{:keys [x1 x2 y1 y2]} shape
|
||||
x-side (mth/abs (- x2 x1))
|
||||
y-side (mth/abs (- y2 y1))
|
||||
max-side (max x-side y-side)]
|
||||
(cond
|
||||
(and (> x1 x2) (> y1 y2))
|
||||
(assoc shape :x2 (- x1 max-side) :y2 (- y1 max-side))
|
||||
:bottom-left
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x2 shape))
|
||||
(+ (:y1 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x2 shape))
|
||||
(- (:y1 shape))))
|
||||
|
||||
(and (< x1 x2) (< y1 y2))
|
||||
(assoc shape :x2 (+ x1 max-side) :y2 (+ y1 max-side))
|
||||
:bottom-right
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x1 shape))
|
||||
(+ (:y1 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x1 shape))
|
||||
(- (:y1 shape))))
|
||||
|
||||
(and (> x1 x2) (< y1 y2))
|
||||
(assoc shape :x2 (- x1 max-side) :y2 (+ y1 max-side))
|
||||
:bottom
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x1 shape))
|
||||
(+ (:y1 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x1 shape))
|
||||
(- (:y1 shape))))
|
||||
|
||||
(and (< x1 x2) (> y1 y2))
|
||||
(assoc shape :x2 (+ x1 max-side) :y2 (- y1 max-side)))))
|
||||
:right
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x1 shape))
|
||||
(+ (:y1 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x1 shape))
|
||||
(- (:y1 shape))))
|
||||
|
||||
:left
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x2 shape))
|
||||
(+ (:y1 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x2 shape))
|
||||
(- (:y1 shape))))))
|
||||
|
||||
|
||||
(defn resize-shape
|
||||
"Apply a resize transformation to a rect-like shape. The shape
|
||||
should have the `width` and `height` attrs, because these attrs
|
||||
are used for the resize transformation.
|
||||
|
||||
Mainly used in drawarea and interactive resize on workspace
|
||||
with the main objective that on the end of resize have a way
|
||||
a calculte the resize ratio with `calculate-scale-ratio`."
|
||||
[vid shape {:keys [x y] :as point} lock?]
|
||||
(case vid
|
||||
:top-left
|
||||
(let [width (- (:x2 shape) x)
|
||||
height (- (:y2 shape) y)
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if lock? (/ width proportion) height)))
|
||||
|
||||
:top-right
|
||||
(let [width (- x (:x1 shape))
|
||||
height (- (:y2 shape) y)
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if lock? (/ width proportion) height)))
|
||||
|
||||
:top
|
||||
(let [width (- (:x2 shape) (:x1 shape))
|
||||
height (- (:y2 shape) y)
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if lock? (/ width proportion) height)))
|
||||
|
||||
:bottom-left
|
||||
(let [width (- (:x2 shape) x)
|
||||
height (- y (:y1 shape))
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if lock? (/ width proportion) height)))
|
||||
|
||||
:bottom-right
|
||||
(let [width (- x (:x1 shape))
|
||||
height (- y (:y1 shape))
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if lock? (/ width proportion) height)))
|
||||
|
||||
:bottom
|
||||
(let [width (- (:x2 shape) (:x1 shape))
|
||||
height (- y (:y1 shape))
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if lock? (/ width proportion) height)))
|
||||
|
||||
:left
|
||||
(let [width (- (:x2 shape) x)
|
||||
height (- (:y2 shape) (:y1 shape))
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if lock? (/ width proportion) height)))
|
||||
|
||||
:right
|
||||
(let [width (- x (:x1 shape))
|
||||
height (- (:y2 shape) (:y1 shape))
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if lock? (/ width proportion) height)))))
|
||||
|
||||
;; --- Setup (Initialize)
|
||||
|
||||
|
@ -521,8 +610,8 @@
|
|||
(selection-rect-generic state shape))))
|
||||
|
||||
(defn- selection-rect-generic
|
||||
[state {:keys [id] :as shape}]
|
||||
(let [{:keys [displacement resize]} (get-in state [:workspace :modifiers id])]
|
||||
[state {:keys [id modifiers] :as shape}]
|
||||
(let [{:keys [displacement resize]} modifiers]
|
||||
(-> (shape->rect-shape shape)
|
||||
(assoc :type :rect :id id)
|
||||
(transform (or resize (gmt/matrix)))
|
||||
|
@ -531,8 +620,8 @@
|
|||
(size))))
|
||||
|
||||
(defn- selection-rect-group
|
||||
[state {:keys [id group items] :as shape}]
|
||||
(let [{:keys [displacement resize]} (get-in state [:workspace :modifiers id])
|
||||
[state {:keys [id group items modifiers] :as shape}]
|
||||
(let [{:keys [displacement resize]} modifiers
|
||||
shapes (->> items
|
||||
(map #(get-in state [:shapes %]))
|
||||
(map #(selection-rect state %)))]
|
||||
|
|
|
@ -66,16 +66,29 @@
|
|||
(-> (l/key :tooltip)
|
||||
(l/derive workspace)))
|
||||
|
||||
(def selected-drawing-shape
|
||||
(-> (l/key :drawing)
|
||||
(l/derive workspace)))
|
||||
|
||||
(def selected-drawing-tool
|
||||
(-> (l/key :drawing-tool)
|
||||
(l/derive workspace)))
|
||||
|
||||
(def selected-edition
|
||||
(-> (l/key :edition)
|
||||
(l/derive workspace)))
|
||||
|
||||
(defn selected-modifiers
|
||||
[id]
|
||||
{:pre [(uuid? id)]}
|
||||
(-> (l/in [:modifiers id])
|
||||
(l/derive workspace)))
|
||||
|
||||
(defn alignment-activated?
|
||||
[state]
|
||||
(let [flags (l/focus ul/workspace-flags state)]
|
||||
(and (contains? flags :grid-indexed)
|
||||
(contains? flags :grid-alignment)
|
||||
(contains? flags :grid))))
|
||||
[flags]
|
||||
(and (contains? flags :grid-indexed)
|
||||
(contains? flags :grid-alignment)
|
||||
(contains? flags :grid)))
|
||||
|
||||
(def selected-alignment
|
||||
(-> (l/lens alignment-activated?)
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
|
||||
(ns uxbox.main.ui.shapes.circle
|
||||
(:require [lentes.core :as l]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.main.ui.shapes.common :as common]
|
||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.util.geom.matrix :as gmt]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.util.mixins :as mx :include-macros true]))
|
||||
|
@ -20,8 +21,8 @@
|
|||
(mx/defc circle-component
|
||||
{:mixins [mx/reactive mx/static]}
|
||||
[{:keys [id] :as shape}]
|
||||
(let [modifiers (mx/react (common/modifiers-ref id))
|
||||
selected (mx/react common/selected-ref)
|
||||
(let [modifiers (mx/react (refs/selected-modifiers id))
|
||||
selected (mx/react refs/selected-shapes)
|
||||
selected? (contains? selected id)
|
||||
on-mouse-down #(common/on-mouse-down % shape selected)
|
||||
shape (assoc shape :modifiers modifiers)]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2016-2017 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.ui.shapes.common
|
||||
(:require [lentes.core :as l]
|
||||
|
@ -15,29 +15,8 @@
|
|||
[uxbox.main.user-events :as uev]
|
||||
[uxbox.main.data.shapes :as uds]
|
||||
[uxbox.main.ui.keyboard :as kbd]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.util.dom :as dom]))
|
||||
|
||||
;; --- Refs
|
||||
|
||||
(def edition-ref
|
||||
(-> (l/in [:edition])
|
||||
(l/derive refs/workspace)))
|
||||
|
||||
;; TODO: replace with refs/selected-drawing-tool
|
||||
(def drawing-state-ref
|
||||
(-> (l/key :drawing-tool)
|
||||
(l/derive refs/workspace)))
|
||||
|
||||
(def selected-ref
|
||||
(-> (l/in [:selected])
|
||||
(l/derive refs/workspace)))
|
||||
|
||||
(defn modifiers-ref
|
||||
[id]
|
||||
(-> (l/in [:modifiers id])
|
||||
(l/derive refs/workspace)))
|
||||
|
||||
;; --- Movement
|
||||
|
||||
;; TODO: implement in the same way as drawing (move under uxbox.main.data.workspace.)
|
||||
|
@ -57,16 +36,16 @@
|
|||
on-move (partial on-move shape)
|
||||
on-stop (partial on-stop shape)]
|
||||
(when @refs/selected-alignment
|
||||
(st/emit! (uds/initial-align-shape shape)))
|
||||
(st/emit! (uds/initial-shape-align shape)))
|
||||
(rx/subscribe stream on-move nil on-stop)))]
|
||||
(run! on-start @selected-ref)))
|
||||
(run! on-start @refs/selected-shapes)))
|
||||
|
||||
;; --- Events
|
||||
|
||||
(defn on-mouse-down
|
||||
[event {:keys [id group] :as shape} selected]
|
||||
(let [selected? (contains? selected id)
|
||||
drawing? @drawing-state-ref]
|
||||
drawing? @refs/selected-drawing-tool]
|
||||
(when-not (:blocked shape)
|
||||
(cond
|
||||
(or drawing?
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2016-2017 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.ui.shapes.group
|
||||
(:require [lentes.core :as l]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.ui.shapes.common :as common]
|
||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||
[uxbox.main.ui.shapes.icon :as icon]
|
||||
|
@ -53,9 +54,11 @@
|
|||
(mx/defc group-component
|
||||
{:mixins [mx/static mx/reactive]}
|
||||
[{:keys [id x y width height group] :as shape}]
|
||||
(let [selected (mx/react common/selected-ref)
|
||||
(let [modifiers (mx/react (refs/selected-modifiers id))
|
||||
selected (mx/react refs/selected-shapes)
|
||||
selected? (contains? selected id)
|
||||
on-mouse-down #(common/on-mouse-down % shape selected)]
|
||||
on-mouse-down #(common/on-mouse-down % shape selected)
|
||||
shape (assoc shape :modifiers modifiers)]
|
||||
[:g.shape.group-shape
|
||||
{:class (when selected? "selected")
|
||||
:on-mouse-down on-mouse-down}
|
||||
|
@ -65,9 +68,12 @@
|
|||
|
||||
(mx/defc group-shape
|
||||
{:mixins [mx/static mx/reactive]}
|
||||
[{:keys [id items tmp-resize-xform tmp-displacement] :as shape} factory]
|
||||
(let [xfmt (cond-> (or tmp-resize-xform (gmt/matrix))
|
||||
tmp-displacement (gmt/translate tmp-displacement))
|
||||
[{:keys [id items modifiers] :as shape} factory]
|
||||
(let [{:keys [resize displacement]} modifiers
|
||||
|
||||
xfmt (cond-> (gmt/matrix)
|
||||
resize (gmt/multiply resize)
|
||||
displacement (gmt/multiply displacement))
|
||||
|
||||
attrs {:id (str "shape-" id)
|
||||
:transform (str xfmt)}]
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.ui.shapes.icon
|
||||
(:require [uxbox.main.ui.shapes.common :as common]
|
||||
(:require [uxbox.main.geom :as geom]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.ui.shapes.common :as common]
|
||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.util.geom.matrix :as gmt]
|
||||
[uxbox.util.geom.point :as gpt]))
|
||||
|
@ -19,8 +20,8 @@
|
|||
(mx/defc icon-component
|
||||
{:mixins [mx/static mx/reactive]}
|
||||
[{:keys [id] :as shape}]
|
||||
(let [modifiers (mx/react (common/modifiers-ref id))
|
||||
selected (mx/react common/selected-ref)
|
||||
(let [modifiers (mx/react (refs/selected-modifiers id))
|
||||
selected (mx/react refs/selected-shapes)
|
||||
selected? (contains? selected id)
|
||||
on-mouse-down #(common/on-mouse-down % shape selected)
|
||||
shape (assoc shape :modifiers modifiers)]
|
||||
|
|
|
@ -8,11 +8,12 @@
|
|||
(:require [beicon.core :as rx]
|
||||
[lentes.core :as l]
|
||||
[potok.core :as ptk]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.ui.shapes.common :as common]
|
||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||
[uxbox.main.data.images :as udi]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.util.geom.matrix :as gmt]))
|
||||
|
||||
|
@ -36,8 +37,8 @@
|
|||
(st/emit! (udi/fetch-image id)))
|
||||
own)}
|
||||
[own {:keys [id image] :as shape}]
|
||||
(let [modifiers (mx/react (common/modifiers-ref id))
|
||||
selected (mx/react common/selected-ref)
|
||||
(let [modifiers (mx/react (refs/selected-modifiers id))
|
||||
selected (mx/react refs/selected-shapes)
|
||||
image (mx/react (image-ref image))
|
||||
selected? (contains? selected id)
|
||||
on-mouse-down #(common/on-mouse-down % shape selected)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
(:require [potok.core :as ptk]
|
||||
[cuerdas.core :as str :include-macros true]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.ui.shapes.common :as common]
|
||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||
[uxbox.main.data.shapes :as uds]
|
||||
|
@ -22,8 +23,8 @@
|
|||
(mx/defc path-component
|
||||
{:mixins [mx/static mx/reactive]}
|
||||
[{:keys [id] :as shape}]
|
||||
(let [modifiers (mx/react (common/modifiers-ref id))
|
||||
selected (mx/react common/selected-ref)
|
||||
(let [modifiers (mx/react (refs/selected-modifiers id))
|
||||
selected (mx/react refs/selected-shapes)
|
||||
selected? (contains? selected id)
|
||||
shape (assoc shape
|
||||
:modifiers modifiers
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.ui.shapes.rect
|
||||
(:require [uxbox.main.ui.shapes.common :as common]
|
||||
(:require [uxbox.main.geom :as geom]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.ui.shapes.common :as common]
|
||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.util.geom.matrix :as gmt]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
|
@ -20,8 +21,8 @@
|
|||
(mx/defc rect-component
|
||||
{:mixins [mx/reactive mx/static]}
|
||||
[{:keys [id] :as shape}]
|
||||
(let [modifiers (mx/react (common/modifiers-ref id))
|
||||
selected (mx/react common/selected-ref)
|
||||
(let [modifiers (mx/react (refs/selected-modifiers id))
|
||||
selected (mx/react refs/selected-shapes)
|
||||
selected? (contains? selected id)
|
||||
on-mouse-down #(common/on-mouse-down % shape selected)
|
||||
shape (assoc shape :modifiers modifiers)]
|
||||
|
@ -32,7 +33,6 @@
|
|||
;; --- Rect Shape
|
||||
|
||||
(defn- rotate
|
||||
;; TODO: revisit, i'm not sure if this function is duplicated.
|
||||
[mt {:keys [x1 y1 x2 y2 width height rotation] :as shape}]
|
||||
(let [x-center (+ x1 (/ width 2))
|
||||
y-center (+ y1 (/ height 2))
|
||||
|
|
|
@ -40,165 +40,27 @@
|
|||
(mapv #(get-in state [:shapes %]) selected)))
|
||||
|
||||
(def ^:private selected-shapes-ref
|
||||
"A customized version of `refs/selected-shapes` that
|
||||
additionally resolves the shapes to the real object
|
||||
instead of just return a set of ids."
|
||||
(-> (l/lens focus-selected-shapes)
|
||||
(l/derive st/state)))
|
||||
|
||||
(def ^:private edition-ref scommon/edition-ref)
|
||||
(def ^:private modifiers-ref
|
||||
(def ^:private selected-modifers-ref
|
||||
"A customized version of `refs/selected-modifiers`
|
||||
that instead of focus to one concrete id, it focuses
|
||||
on the whole map."
|
||||
(-> (l/key :modifiers)
|
||||
(l/derive refs/workspace)))
|
||||
|
||||
;; --- Resize Implementation
|
||||
|
||||
(defn- calculate-scale-ratio
|
||||
"Calculate the scale factor from one shape to an other."
|
||||
[origin final]
|
||||
[(/ (:width final) (:width origin))
|
||||
(/ (:height final) (:height origin))])
|
||||
|
||||
(defn generate-resize-matrix
|
||||
"Generate the resize transformation matrix given
|
||||
a corner-id, shape and the scale factor vector."
|
||||
[vid shape [scalex scaley]]
|
||||
(case vid
|
||||
:top-left
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x2 shape))
|
||||
(+ (:y2 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x2 shape))
|
||||
(- (:y2 shape))))
|
||||
|
||||
:top-right
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x1 shape))
|
||||
(+ (:y2 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x1 shape))
|
||||
(- (:y2 shape))))
|
||||
|
||||
:top
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x1 shape))
|
||||
(+ (:y2 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x1 shape))
|
||||
(- (:y2 shape))))
|
||||
|
||||
:bottom-left
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x2 shape))
|
||||
(+ (:y1 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x2 shape))
|
||||
(- (:y1 shape))))
|
||||
|
||||
:bottom-right
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x1 shape))
|
||||
(+ (:y1 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x1 shape))
|
||||
(- (:y1 shape))))
|
||||
|
||||
:bottom
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x1 shape))
|
||||
(+ (:y1 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x1 shape))
|
||||
(- (:y1 shape))))
|
||||
|
||||
:right
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x1 shape))
|
||||
(+ (:y1 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x1 shape))
|
||||
(- (:y1 shape))))
|
||||
|
||||
:left
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate (+ (:x2 shape))
|
||||
(+ (:y1 shape)))
|
||||
(gmt/scale scalex scaley)
|
||||
(gmt/translate (- (:x2 shape))
|
||||
(- (:y1 shape))))))
|
||||
|
||||
|
||||
(defn- resize-shape
|
||||
[vid shape {:keys [x y] :as point} ctrl?]
|
||||
(case vid
|
||||
:top-left
|
||||
(let [width (- (:x2 shape) x)
|
||||
height (- (:y2 shape) y)
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if ctrl? (/ width proportion) height)))
|
||||
|
||||
:top-right
|
||||
(let [width (- x (:x1 shape))
|
||||
height (- (:y2 shape) y)
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if ctrl? (/ width proportion) height)))
|
||||
|
||||
:top
|
||||
(let [width (- (:x2 shape) (:x1 shape))
|
||||
height (- (:y2 shape) y)
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if ctrl? (/ width proportion) height)))
|
||||
|
||||
:bottom-left
|
||||
(let [width (- (:x2 shape) x)
|
||||
height (- y (:y1 shape))
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if ctrl? (/ width proportion) height)))
|
||||
|
||||
:bottom-right
|
||||
(let [width (- x (:x1 shape))
|
||||
height (- y (:y1 shape))
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if ctrl? (/ width proportion) height)))
|
||||
|
||||
:bottom
|
||||
(let [width (- (:x2 shape) (:x1 shape))
|
||||
height (- y (:y1 shape))
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if ctrl? (/ width proportion) height)))
|
||||
|
||||
:left
|
||||
(let [width (- (:x2 shape) x)
|
||||
height (- (:y2 shape) (:y1 shape))
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if ctrl? (/ width proportion) height)))
|
||||
|
||||
:right
|
||||
(let [width (- x (:x1 shape))
|
||||
height (- (:y2 shape) (:y1 shape))
|
||||
proportion (:proportion shape)]
|
||||
(assoc shape
|
||||
:width width
|
||||
:height (if ctrl? (/ width proportion) height)))))
|
||||
|
||||
(defn- start-resize
|
||||
[vid ids shape]
|
||||
(letfn [(on-resize [shape [point lock?]]
|
||||
(let [result (resize-shape vid shape point lock?)
|
||||
scale (calculate-scale-ratio shape result)
|
||||
mtx (generate-resize-matrix vid shape scale)
|
||||
(let [result (geom/resize-shape vid shape point lock?)
|
||||
scale (geom/calculate-scale-ratio shape result)
|
||||
mtx (geom/generate-resize-matrix vid shape scale)
|
||||
xfm (map #(uds/apply-temporal-resize % mtx))]
|
||||
(apply st/emit! (sequence xfm ids))))
|
||||
|
||||
|
@ -343,9 +205,11 @@
|
|||
(mx/defc multiple-selection-handlers
|
||||
{:mixins [mx/static]}
|
||||
[[shape & rest :as shapes] modifiers zoom]
|
||||
(let [selection (-> (map #(geom/selection-rect %) shapes)
|
||||
(geom/shapes->rect-shape)
|
||||
(geom/selection-rect))
|
||||
(let [selection (->> shapes
|
||||
(map #(assoc % :modifiers (get modifiers (:id %))))
|
||||
(map #(geom/selection-rect %))
|
||||
(geom/shapes->rect-shape)
|
||||
(geom/selection-rect))
|
||||
shape (geom/shapes->rect-shape shapes)
|
||||
on-click #(do (dom/stop-propagation %2)
|
||||
(start-resize %1 (map :id shapes) shape))]
|
||||
|
@ -353,7 +217,7 @@
|
|||
|
||||
(mx/defc single-selection-handlers
|
||||
{:mixins [mx/static]}
|
||||
[{:keys [id] :as shape} modifiers zoom]
|
||||
[{:keys [id] :as shape} zoom]
|
||||
(let [on-click #(do (dom/stop-propagation %2)
|
||||
(start-resize %1 #{id} shape))
|
||||
shape (geom/selection-rect shape)]
|
||||
|
@ -377,11 +241,14 @@
|
|||
{:mixins [mx/reactive mx/static]}
|
||||
[]
|
||||
(let [shapes (mx/react selected-shapes-ref)
|
||||
modifiers (mx/react modifiers-ref)
|
||||
edition? (mx/react edition-ref)
|
||||
modifiers (mx/react selected-modifers-ref)
|
||||
;; Edition is a workspace global flag
|
||||
;; because only one shape can be on
|
||||
;; the edition mode.
|
||||
edition? (mx/react refs/selected-edition)
|
||||
zoom (mx/react refs/selected-zoom)
|
||||
num (count shapes)
|
||||
{:keys [type] :as shape} (first shapes)]
|
||||
{:keys [id type] :as shape} (first shapes)]
|
||||
(cond
|
||||
(zero? num)
|
||||
nil
|
||||
|
@ -390,12 +257,15 @@
|
|||
(multiple-selection-handlers shapes modifiers zoom)
|
||||
|
||||
(and (= type :text) edition?)
|
||||
(text-edition-selection-handlers shape zoom)
|
||||
(-> (assoc shape :modifiers (get modifiers id))
|
||||
(text-edition-selection-handlers zoom))
|
||||
|
||||
(= type :path)
|
||||
(if (= @edition-ref (:id shape))
|
||||
(if (= @refs/selected-edition (:id shape))
|
||||
(path-edition-selection-handlers shape zoom)
|
||||
(single-selection-handlers shape modifiers zoom))
|
||||
(-> (assoc shape :modifiers (get modifiers id))
|
||||
(single-selection-handlers zoom)))
|
||||
|
||||
:else
|
||||
(single-selection-handlers shape modifiers zoom))))
|
||||
(-> (assoc shape :modifiers (get modifiers id))
|
||||
(single-selection-handlers zoom)))))
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
[potok.core :as ptk]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.data.shapes :as uds]
|
||||
[uxbox.main.ui.shapes.common :as common]
|
||||
[uxbox.main.ui.shapes.attrs :as attrs]
|
||||
|
@ -25,8 +26,8 @@
|
|||
(defn handle-mouse-down
|
||||
[event {:keys [id group] :as shape} selected]
|
||||
(if (and (not (:blocked shape))
|
||||
(or @common/drawing-state-ref
|
||||
@common/edition-ref
|
||||
(or @refs/selected-drawing-tool
|
||||
@refs/selected-edition
|
||||
(and group (:locked (geom/resolve-parent shape)))))
|
||||
(dom/stop-propagation event)
|
||||
(common/on-mouse-down event shape selected)))
|
||||
|
@ -40,10 +41,12 @@
|
|||
(mx/defcs text-component
|
||||
{:mixins [mx/static mx/reactive]}
|
||||
[own {:keys [id x1 y1 content group] :as shape}]
|
||||
(let [selected (mx/react common/selected-ref)
|
||||
edition? (= (mx/react common/edition-ref) id)
|
||||
(let [modifiers (mx/react (refs/selected-modifiers id))
|
||||
selected (mx/react refs/selected-shapes)
|
||||
edition? (= (mx/react refs/selected-edition) id)
|
||||
selected? (and (contains? selected id)
|
||||
(= (count selected) 1))]
|
||||
(= (count selected) 1))
|
||||
shape (assoc shape :modifiers modifiers)]
|
||||
(letfn [(on-mouse-down [event]
|
||||
(handle-mouse-down event shape selected))
|
||||
(on-double-click [event]
|
||||
|
@ -155,26 +158,20 @@
|
|||
(mx/defc text-shape-wrapper
|
||||
{:did-mount text-shape-wrapper-did-mount
|
||||
:did-remount text-shape-wrapper-did-remount}
|
||||
[{:keys [id tmp-resize-xform tmp-displacement drawing?] :as shape}]
|
||||
(let [xfmt (cond-> (gmt/matrix)
|
||||
tmp-displacement (gmt/translate tmp-displacement)
|
||||
tmp-resize-xform (gmt/multiply tmp-resize-xform))
|
||||
[{:keys [id modifiers] :as shape}]
|
||||
(let [{:keys [displacement resize]} modifiers
|
||||
xfmt (cond-> (gmt/matrix)
|
||||
displacement (gmt/multiply displacement)
|
||||
resize (gmt/multiply resize))
|
||||
|
||||
{:keys [x1 y1 width height] :as shape} (-> (geom/transform shape xfmt)
|
||||
(geom/size))
|
||||
attrs {:x x1
|
||||
props {:x x1
|
||||
:y y1
|
||||
:id (str id)
|
||||
:ref "fobject"
|
||||
:width width
|
||||
:height height}
|
||||
props (merge attrs
|
||||
(when drawing?
|
||||
{:style {:stroke "#333"
|
||||
:stroke-width "0.5"
|
||||
:stroke-opacity "0.5"
|
||||
:fill "transparent"}}))]
|
||||
|
||||
:height height}]
|
||||
[:foreignObject props]))
|
||||
|
||||
;; --- Text Shape Html
|
||||
|
|
|
@ -23,12 +23,6 @@
|
|||
[uxbox.util.geom.path :as path]
|
||||
[uxbox.util.dom :as dom]))
|
||||
|
||||
;; --- Refs
|
||||
|
||||
(def drawing-shape
|
||||
(-> (l/key :drawing)
|
||||
(l/derive refs/workspace)))
|
||||
|
||||
;; --- Components
|
||||
|
||||
(declare generic-draw-area)
|
||||
|
@ -37,25 +31,26 @@
|
|||
(mx/defc draw-area
|
||||
{:mixins [mx/static mx/reactive]}
|
||||
[zoom]
|
||||
(when-let [shape (mx/react drawing-shape)]
|
||||
(if (= (:type shape) :path)
|
||||
(path-draw-area shape)
|
||||
(generic-draw-area shape zoom))))
|
||||
(when-let [{:keys [id] :as shape} (mx/react refs/selected-drawing-shape)]
|
||||
(let [modifiers (mx/react (refs/selected-modifiers id))]
|
||||
(if (= (:type shape) :path)
|
||||
(path-draw-area shape)
|
||||
(-> (assoc shape :modifiers modifiers)
|
||||
(generic-draw-area zoom))))))
|
||||
|
||||
(mx/defc generic-draw-area
|
||||
[shape zoom]
|
||||
(let [{:keys [x1 y1 width height]} (geom/selection-rect shape)]
|
||||
[:g
|
||||
(-> (assoc shape :drawing? true)
|
||||
(shapes/render-component))
|
||||
(shapes/render-component shape)
|
||||
[:rect.main {:x x1 :y y1
|
||||
:width width
|
||||
:height height
|
||||
:stroke-dasharray (str (/ 5.0 zoom) "," (/ 5 zoom))
|
||||
:style {:stroke "#333" :fill "transparent"
|
||||
:style {:stroke "#333"
|
||||
:fill "transparent"
|
||||
:stroke-opacity "1"}}]]))
|
||||
|
||||
|
||||
(mx/defc path-draw-area
|
||||
[{:keys [segments] :as shape}]
|
||||
(letfn [(on-click [event]
|
||||
|
@ -68,8 +63,7 @@
|
|||
(st/emit! (udw/set-tooltip nil)))]
|
||||
(when-let [{:keys [x y] :as segment} (first segments)]
|
||||
[:g
|
||||
(-> (assoc shape :drawing? true)
|
||||
(shapes/render-component))
|
||||
(shapes/render-component shape)
|
||||
(when-not (:free shape)
|
||||
[:circle.close-bezier {:cx x
|
||||
:cy y
|
||||
|
|
Loading…
Add table
Reference in a new issue