0
Fork 0
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:
Andrey Antukh 2017-02-27 20:23:51 +01:00
parent b7253b7fd5
commit f82ddac72d
No known key found for this signature in database
GPG key ID: 4DFEBCB8316A8B95
16 changed files with 291 additions and 321 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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