From f82ddac72dc592c1d26dcbeebc69ac1a806f5f3c Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 27 Feb 2017 20:23:51 +0100 Subject: [PATCH] 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. --- frontend/src/uxbox/main/data/shapes.cljs | 2 +- frontend/src/uxbox/main/data/shapes_impl.cljs | 5 +- frontend/src/uxbox/main/data/workspace.cljs | 1 - .../uxbox/main/data/workspace/drawing.cljs | 38 +++- frontend/src/uxbox/main/geom.cljs | 211 +++++++++++++----- frontend/src/uxbox/main/refs.cljs | 23 +- frontend/src/uxbox/main/ui/shapes/circle.cljs | 7 +- frontend/src/uxbox/main/ui/shapes/common.cljs | 29 +-- frontend/src/uxbox/main/ui/shapes/group.cljs | 18 +- frontend/src/uxbox/main/ui/shapes/icon.cljs | 9 +- frontend/src/uxbox/main/ui/shapes/image.cljs | 7 +- frontend/src/uxbox/main/ui/shapes/path.cljs | 5 +- frontend/src/uxbox/main/ui/shapes/rect.cljs | 10 +- .../src/uxbox/main/ui/shapes/selection.cljs | 188 +++------------- frontend/src/uxbox/main/ui/shapes/text.cljs | 33 ++- .../src/uxbox/main/ui/workspace/drawarea.cljs | 26 +-- 16 files changed, 291 insertions(+), 321 deletions(-) diff --git a/frontend/src/uxbox/main/data/shapes.cljs b/frontend/src/uxbox/main/data/shapes.cljs index 36b49868e..ef31d05b7 100644 --- a/frontend/src/uxbox/main/data/shapes.cljs +++ b/frontend/src/uxbox/main/data/shapes.cljs @@ -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 diff --git a/frontend/src/uxbox/main/data/shapes_impl.cljs b/frontend/src/uxbox/main/data/shapes_impl.cljs index 598445bf4..1c7d42678 100644 --- a/frontend/src/uxbox/main/data/shapes_impl.cljs +++ b/frontend/src/uxbox/main/data/shapes_impl.cljs @@ -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)))) diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index 74865546c..681623101 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -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) diff --git a/frontend/src/uxbox/main/data/workspace/drawing.cljs b/frontend/src/uxbox/main/data/workspace/drawing.cljs index 0c9889785..0feed4ea5 100644 --- a/frontend/src/uxbox/main/data/workspace/drawing.cljs +++ b/frontend/src/uxbox/main/data/workspace/drawing.cljs @@ -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 [] diff --git a/frontend/src/uxbox/main/geom.cljs b/frontend/src/uxbox/main/geom.cljs index 9ee43c887..c0c5526ad 100644 --- a/frontend/src/uxbox/main/geom.cljs +++ b/frontend/src/uxbox/main/geom.cljs @@ -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 %)))] diff --git a/frontend/src/uxbox/main/refs.cljs b/frontend/src/uxbox/main/refs.cljs index 0008c1b77..dc674ea3c 100644 --- a/frontend/src/uxbox/main/refs.cljs +++ b/frontend/src/uxbox/main/refs.cljs @@ -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?) diff --git a/frontend/src/uxbox/main/ui/shapes/circle.cljs b/frontend/src/uxbox/main/ui/shapes/circle.cljs index 74306dc95..93fe703a3 100644 --- a/frontend/src/uxbox/main/ui/shapes/circle.cljs +++ b/frontend/src/uxbox/main/ui/shapes/circle.cljs @@ -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)] diff --git a/frontend/src/uxbox/main/ui/shapes/common.cljs b/frontend/src/uxbox/main/ui/shapes/common.cljs index 8efc519f4..9d28a1247 100644 --- a/frontend/src/uxbox/main/ui/shapes/common.cljs +++ b/frontend/src/uxbox/main/ui/shapes/common.cljs @@ -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 +;; Copyright (c) 2016-2017 Andrey Antukh (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? diff --git a/frontend/src/uxbox/main/ui/shapes/group.cljs b/frontend/src/uxbox/main/ui/shapes/group.cljs index 4b120b276..bf0e53dc9 100644 --- a/frontend/src/uxbox/main/ui/shapes/group.cljs +++ b/frontend/src/uxbox/main/ui/shapes/group.cljs @@ -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 +;; Copyright (c) 2016-2017 Andrey Antukh (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)}] diff --git a/frontend/src/uxbox/main/ui/shapes/icon.cljs b/frontend/src/uxbox/main/ui/shapes/icon.cljs index b74a9fa70..353fbb544 100644 --- a/frontend/src/uxbox/main/ui/shapes/icon.cljs +++ b/frontend/src/uxbox/main/ui/shapes/icon.cljs @@ -5,9 +5,10 @@ ;; Copyright (c) 2016 Andrey Antukh (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)] diff --git a/frontend/src/uxbox/main/ui/shapes/image.cljs b/frontend/src/uxbox/main/ui/shapes/image.cljs index 235c92d0f..8f50e7f57 100644 --- a/frontend/src/uxbox/main/ui/shapes/image.cljs +++ b/frontend/src/uxbox/main/ui/shapes/image.cljs @@ -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) diff --git a/frontend/src/uxbox/main/ui/shapes/path.cljs b/frontend/src/uxbox/main/ui/shapes/path.cljs index 87c69549d..525810c77 100644 --- a/frontend/src/uxbox/main/ui/shapes/path.cljs +++ b/frontend/src/uxbox/main/ui/shapes/path.cljs @@ -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 diff --git a/frontend/src/uxbox/main/ui/shapes/rect.cljs b/frontend/src/uxbox/main/ui/shapes/rect.cljs index 6f5778ec3..8d16abb79 100644 --- a/frontend/src/uxbox/main/ui/shapes/rect.cljs +++ b/frontend/src/uxbox/main/ui/shapes/rect.cljs @@ -5,9 +5,10 @@ ;; Copyright (c) 2016 Andrey Antukh (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)) diff --git a/frontend/src/uxbox/main/ui/shapes/selection.cljs b/frontend/src/uxbox/main/ui/shapes/selection.cljs index a7b8cde49..9872b9d4b 100644 --- a/frontend/src/uxbox/main/ui/shapes/selection.cljs +++ b/frontend/src/uxbox/main/ui/shapes/selection.cljs @@ -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))))) diff --git a/frontend/src/uxbox/main/ui/shapes/text.cljs b/frontend/src/uxbox/main/ui/shapes/text.cljs index 120c52fe1..bf00aa4f4 100644 --- a/frontend/src/uxbox/main/ui/shapes/text.cljs +++ b/frontend/src/uxbox/main/ui/shapes/text.cljs @@ -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 diff --git a/frontend/src/uxbox/main/ui/workspace/drawarea.cljs b/frontend/src/uxbox/main/ui/workspace/drawarea.cljs index 41c216a5a..2ceb37d34 100644 --- a/frontend/src/uxbox/main/ui/workspace/drawarea.cljs +++ b/frontend/src/uxbox/main/ui/workspace/drawarea.cljs @@ -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