From 338a0b97ac9facb707f7f3bb1587cf84c1b69739 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 15 Jan 2020 00:15:23 +0100 Subject: [PATCH] :construction: Start working on shape rotation (with mouse handlers). --- .../styles/main/partials/workspace.scss | 1 + frontend/src/uxbox/main/ui/shapes/rect.cljs | 14 ++- .../uxbox/main/ui/workspace/selection.cljs | 108 +++++++++++++----- frontend/src/uxbox/util/geom/point.cljs | 2 +- 4 files changed, 89 insertions(+), 36 deletions(-) diff --git a/frontend/resources/styles/main/partials/workspace.scss b/frontend/resources/styles/main/partials/workspace.scss index aa56e0363..91c6f2a17 100644 --- a/frontend/resources/styles/main/partials/workspace.scss +++ b/frontend/resources/styles/main/partials/workspace.scss @@ -89,6 +89,7 @@ g.controls { rect.main { pointer-events: none; } + circle.rotate { cursor: ns-resize; } // TODO circle.top-left { cursor: nwse-resize; } circle.bottom-right { cursor: nwse-resize; } circle.top-right { cursor: nesw-resize; } diff --git a/frontend/src/uxbox/main/ui/shapes/rect.cljs b/frontend/src/uxbox/main/ui/shapes/rect.cljs index 1b2184072..4d8c6869a 100644 --- a/frontend/src/uxbox/main/ui/shapes/rect.cljs +++ b/frontend/src/uxbox/main/ui/shapes/rect.cljs @@ -49,17 +49,21 @@ {:keys [x y width height]} shape - transform (when (pos? rotation) - (str (rotate (gmt/matrix) shape))) + ;; transform (when (pos? rotation) + ;; (str (rotate (gmt/matrix) shape))) - moving? (boolean modifier-mtx) + transform (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 :id (str "shape-" id) - :className (classnames :move-cursor moving?) :width width :height height - :transform transform))] + ;; :transform transform + ))] [:& "rect" props])) diff --git a/frontend/src/uxbox/main/ui/workspace/selection.cljs b/frontend/src/uxbox/main/ui/workspace/selection.cljs index 203ebee0a..6d51eabfc 100644 --- a/frontend/src/uxbox/main/ui/workspace/selection.cljs +++ b/frontend/src/uxbox/main/ui/workspace/selection.cljs @@ -10,6 +10,7 @@ (:require [beicon.core :as rx] [lentes.core :as l] + [cuerdas.core :as str] [potok.core :as ptk] [rumext.alpha :as mf] [uxbox.main.data.workspace :as dw] @@ -21,16 +22,6 @@ [uxbox.util.dom :as dom] [uxbox.util.geom.point :as gpt])) -;; --- Refs & Constants - -(def ^:private +circle-props+ - {:r 6 - :style {:fillOpacity "1" - :strokeWidth "2px" - :vectorEffect "non-scaling-stroke"} - :fill "rgba(49,239,184,.7)" - :stroke "#31EFB8"}) - ;; --- Resize Implementation (defn- start-resize @@ -75,6 +66,37 @@ (rx/of (dw/materialize-temporal-modifier-in-bulk ids) ::dw/page-data-update))))))) +(defn start-rotate + [shape] + (ptk/reify ::start-rotate + ptk/WatchEvent + (watch [_ state stream] + (let [shape (geom/shape->rect-shape shape) + stoper (rx/filter ms/mouse-up? stream) + center (gpt/point (+ (:x shape) (/ (:width shape) 2)) + (+ (:y shape) (/ (:height shape) 2)))] + + (rx/concat + (->> ms/mouse-position + ;; (rx/map apply-zoom) + ;; (rx/mapcat apply-grid-alignment) + (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) + (+ 360 angle) + angle) + modval (mod angle 90) + angle (if ctrl? + (if (< 50 modval) + (+ angle (- 90 modval)) + (- angle modval)) + angle)] + (dw/update-shape (:id shape) {:rotation angle})))) + (rx/take-until stoper))))))) + ;; --- Controls (Component) (def ^:private handler-size-threshold @@ -98,52 +120,73 @@ :cy cy}]) (mf/defc controls - [{:keys [shape zoom on-click] :as props}] + [{:keys [shape zoom on-resize on-rotate] :as props}] (let [{:keys [x y width height]} shape - radius (if (> (max width height) handler-size-threshold) 6.0 4.0)] - [:g.controls + 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} [: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)}] [:& control-item {:class "top" - :on-click #(on-click :top %) + :on-click #(on-resize :top %) :r (/ radius zoom) :cx (+ x (/ width 2)) :cy (- y 2)}] - [:& control-item {:on-click #(on-click :right %) + [:& control-item {:on-click #(on-resize :right %) :r (/ radius zoom) :cy (+ y (/ height 2)) :cx (+ x width 1) :class "right"}] - [:& control-item {:on-click #(on-click :bottom %) + [:& control-item {:on-click #(on-resize :bottom %) :r (/ radius zoom) :cx (+ x (/ width 2)) :cy (+ y height 2) :class "bottom"}] - [:& control-item {:on-click #(on-click :left %) + [:& control-item {:on-click #(on-resize :left %) :r (/ radius zoom) :cy (+ y (/ height 2)) :cx (- x 3) :class "left"}] - [:& control-item {:on-click #(on-click :top-left %) + [:& control-item {:on-click #(on-resize :top-left %) :r (/ radius zoom) :cx x :cy y :class "top-left"}] - [:& control-item {:on-click #(on-click :top-right %) - :r (/ radius zoom) - :cx (+ x width) - :cy y - :class "top-right"}] - [:& control-item {:on-click #(on-click :bottom-left %) + [:& control-item {:on-click #(on-resize :top-right %) + :r (/ radius zoom) + :cx (+ x width) + :cy y + :class "top-right"}] + [:& control-item {:on-click #(on-resize :bottom-left %) :r (/ radius zoom) :cx x :cy (+ y height) :class "bottom-left"}] - [:& control-item {:on-click #(on-click :bottom-right %) + [:& control-item {:on-click #(on-resize :bottom-right %) :r (/ radius zoom) :cx (+ x width) :cy (+ y height) @@ -195,11 +238,11 @@ (map #(geom/selection-rect %)) (geom/shapes->rect-shape) (geom/selection-rect)) - on-click #(do (dom/stop-propagation %2) + on-resize #(do (dom/stop-propagation %2) (st/emit! (start-resize %1 selected shape)))] [:& controls {:shape shape :zoom zoom - :on-click on-click}])) + :on-resize on-resize}])) (mf/defc text-edition-selection-handlers [{:keys [shape zoom] :as props}] @@ -216,10 +259,15 @@ (mf/defc single-selection-handlers [{:keys [shape zoom] :as props}] - (let [on-click #(do (dom/stop-propagation %2) - (st/emit! (start-resize %1 #{(:id shape)} 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)] - [:& controls {:shape shape :zoom zoom :on-click on-click}])) + [:& controls {:shape shape + :zoom zoom + :on-rotate on-rotate + :on-resize on-resize}])) (mf/defc selection-handlers [{:keys [selected edition zoom] :as props}] diff --git a/frontend/src/uxbox/util/geom/point.cljs b/frontend/src/uxbox/util/geom/point.cljs index 27af79e90..9a04c9e01 100644 --- a/frontend/src/uxbox/util/geom/point.cljs +++ b/frontend/src/uxbox/util/geom/point.cljs @@ -116,7 +116,7 @@ If the second vector is not provided, the angle will be measured from x-axis." ([p] - {:pre [(point? p)]} + (-> (mth/atan2 (:y p) (:x p)) (mth/degrees))) ([p center]