From 84007e6ad1b95254f72b031ad18def0879c2577c Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 22 Dec 2020 17:44:51 +0100 Subject: [PATCH] :sparkles: Allows rotation for shapes --- common/app/common/data.cljc | 4 +- common/app/common/geom/shapes.cljc | 5 +- common/app/common/geom/shapes/transforms.cljc | 29 +++++++---- frontend/src/app/main/data/workspace.cljs | 10 ++-- .../app/main/data/workspace/drawing/path.cljs | 49 ++++++++++++++----- .../app/main/data/workspace/transforms.cljs | 14 ++++++ frontend/src/app/main/ui/shapes/path.cljs | 6 +-- .../src/app/main/ui/workspace/selection.cljs | 27 +++++----- .../ui/workspace/shapes/bounding_box.cljs | 7 +-- .../app/main/ui/workspace/shapes/outline.cljs | 3 +- 10 files changed, 105 insertions(+), 49 deletions(-) diff --git a/common/app/common/data.cljc b/common/app/common/data.cljc index 99a643129..972843b5d 100644 --- a/common/app/common/data.cljc +++ b/common/app/common/data.cljc @@ -293,7 +293,9 @@ "Function that checks if a number is nil or nan. Will return 0 when not valid and the number otherwise." [v] - (if (or (not v) (mth/nan? v)) 0 v)) + (if (or (not v) + (not (mth/finite? v)) + (mth/nan? v)) 0 v)) (defmacro export diff --git a/common/app/common/geom/shapes.cljc b/common/app/common/geom/shapes.cljc index 22b5dd328..50d9e07bb 100644 --- a/common/app/common/geom/shapes.cljc +++ b/common/app/common/geom/shapes.cljc @@ -48,7 +48,7 @@ (us/assert #{:width :height} attr) (us/assert number? value) (let [{:keys [proportion proportion-lock]} shape - size (select-keys shape [:width :height]) + size (select-keys (:selrect shape) [:width :height]) new-size (if-not proportion-lock (assoc size attr value) (if (= attr :width) @@ -260,6 +260,7 @@ (d/export gco/center-shape) (d/export gco/center-selrect) (d/export gco/center-rect) +(d/export gco/center-points) (d/export gpr/rect->selrect) (d/export gpr/rect->points) (d/export gpr/points->selrect) @@ -268,7 +269,9 @@ (d/export gtr/transform-point-center) (d/export gtr/transform-rect) (d/export gtr/update-group-selrect) +(d/export gtr/transform-points) ;; PATHS (d/export gsp/content->points) (d/export gsp/content->selrect) +(d/export gsp/transform-content) diff --git a/common/app/common/geom/shapes/transforms.cljc b/common/app/common/geom/shapes/transforms.cljc index 0430f0ea2..d126c8cdd 100644 --- a/common/app/common/geom/shapes/transforms.cljc +++ b/common/app/common/geom/shapes/transforms.cljc @@ -14,7 +14,13 @@ [app.common.geom.shapes.common :as gco] [app.common.geom.shapes.path :as gpa] [app.common.geom.shapes.rect :as gpr] - [app.common.math :as mth])) + [app.common.math :as mth] + [app.common.data :as d])) + +(defn- modif-rotation [shape] + (let [cur-rotation (d/check-num (:rotation shape)) + delta-angle (d/check-num (get-in shape [:modifiers :rotation]))] + (mod (+ cur-rotation delta-angle) 360))) (defn transform-matrix "Returns a transformation matrix without changing the shape properties. @@ -191,18 +197,23 @@ (defn apply-transform-path [shape transform] (let [content (gpa/transform-content (:content shape) transform) - selrect (gpa/content->selrect content) - points (gpr/rect->points selrect) - ;;rotation (mod (+ (:rotation shape 0) - ;; (or (get-in shape [:modifiers :rotation]) 0)) - ;; 360) - ] + + ;; Calculate the new selrect by "unrotate" the shape + rotation (modif-rotation shape) + center (gpt/transform (gco/center-shape shape) transform) + content-rotated (gpa/transform-content content (gmt/rotate-matrix (- rotation) center)) + selrect (gpa/content->selrect content-rotated) + + ;; Transform the points + points (-> (:points shape) + (transform-points transform))] (assoc shape :content content :points points :selrect selrect - ;;:rotation rotation - ))) + :transform (gmt/rotate-matrix rotation) + :transform-inverse (gmt/rotate-matrix (- rotation)) + :rotation rotation))) (defn apply-transform-rect "Given a new set of points transformed, set up the rectangle so it keeps diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index d68a1c24e..638731dc1 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1038,9 +1038,12 @@ (let [page-id (:current-page-id state) objects (dwc/lookup-page-objects state page-id) shape (get objects id) - cpos (gpt/point (:x shape) (:y shape)) - pos (gpt/point (or (:x position) (:x shape)) - (or (:y position) (:y shape))) + + bbox (-> shape :points gsh/points->selrect) + + cpos (gpt/point (:x bbox) (:y bbox)) + pos (gpt/point (or (:x position) (:x bbox)) + (or (:y position) (:y bbox))) displ (gmt/translate-matrix (gpt/subtract pos cpos))] (rx/of (dwt/set-modifiers [id] {:displacement displ}) (dwt/apply-modifiers [id])))))) @@ -1544,6 +1547,7 @@ (d/export dwt/start-move-selected) (d/export dwt/move-selected) (d/export dwt/set-rotation) +(d/export dwt/increase-rotation) (d/export dwt/set-modifiers) (d/export dwt/apply-modifiers) (d/export dwt/update-dimensions) diff --git a/frontend/src/app/main/data/workspace/drawing/path.cljs b/frontend/src/app/main/data/workspace/drawing/path.cljs index 4599e903b..25515a45c 100644 --- a/frontend/src/app/main/data/workspace/drawing/path.cljs +++ b/frontend/src/app/main/data/workspace/drawing/path.cljs @@ -16,6 +16,7 @@ [app.common.math :as mth] [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] + [app.common.geom.matrix :as gmt] [app.util.data :as ud] [app.common.data :as cd] [app.util.geom.path :as ugp] @@ -87,12 +88,37 @@ [:workspace-drawing :object]) path))) +(defn- points->components [shape content] + (let [rotation (:rotation shape 0) + center (gsh/center-shape shape) + content-rotated (gsh/transform-content content (gmt/rotate-matrix (- rotation) center)) + + ;; Calculates the new selrect with points given the old center + points (-> (gsh/content->selrect content-rotated) + (gsh/rect->points) + (gsh/transform-points center (:transform shape (gmt/matrix)))) + + points-center (gsh/center-points points) + + ;; Points is now the selrect but the center is different so we can create the selrect + ;; through points + selrect (-> points + (gsh/transform-points points-center (:transform-inverse shape (gmt/matrix))) + (gsh/points->selrect))] + [points selrect])) + (defn update-selrect "Updates the selrect and points for a path" [shape] - (let [selrect (gsh/content->selrect (:content shape)) - points (gsh/rect->points selrect)] - (assoc shape :points points :selrect selrect))) + (if (= (:rotation shape 0) 0) + (let [content (:content shape) + selrect (gsh/content->selrect content) + points (gsh/rect->points selrect)] + (assoc shape :points points :selrect selrect)) + + (let [content (:content shape) + [points selrect] (points->components shape content)] + (assoc shape :points points :selrect selrect)))) (defn closest-angle [angle] (cond @@ -157,13 +183,12 @@ ;; TODO: Enter now finish path but can finish drawing/editing as well (= enter-keycode (:key event))))) -(defn generate-path-changes [page-id shape-id old-content new-content] +(defn generate-path-changes [page-id shape old-content new-content] (us/verify ::content old-content) (us/verify ::content new-content) - (let [old-selrect (gsh/content->selrect old-content) - old-points (gsh/rect->points old-selrect) - new-selrect (gsh/content->selrect new-content) - new-points (gsh/rect->points new-selrect) + (let [shape-id (:id shape) + [old-points old-selrect] (points->components shape old-content) + [new-points new-selrect] (points->components shape new-content) rch [{:type :mod-obj :id shape-id @@ -393,7 +418,7 @@ shape (get-in state (get-path state)) selected-points (get-in state [:workspace-local :edit-path id :selected-points] #{}) new-content (reduce ugp/make-corner-point (:content shape) selected-points) - [rch uch] (generate-path-changes page-id id (:content shape) new-content)] + [rch uch] (generate-path-changes page-id shape (:content shape) new-content)] (rx/of (dwc/commit-changes rch uch {:commit-local? true})))))) (defn make-curve [] @@ -405,7 +430,7 @@ shape (get-in state (get-path state)) selected-points (get-in state [:workspace-local :edit-path id :selected-points] #{}) new-content (reduce ugp/make-curve-point (:content shape) selected-points) - [rch uch] (generate-path-changes page-id id (:content shape) new-content)] + [rch uch] (generate-path-changes page-id shape (:content shape) new-content)] (rx/of (dwc/commit-changes rch uch {:commit-local? true})))))) (defn path-handler-enter [index prefix] @@ -552,7 +577,7 @@ shape (get-in state (get-path state)) content-modifiers (get-in state [:workspace-local :edit-path id :content-modifiers]) new-content (ugp/apply-content-modifiers (:content shape) content-modifiers) - [rch uch] (generate-path-changes page-id (:id shape) (:content shape) new-content)] + [rch uch] (generate-path-changes page-id shape (:content shape) new-content)] (rx/of (dwc/commit-changes rch uch {:commit-local? true}) (fn [state] (update-in state [:workspace-local :edit-path id] dissoc :content-modifiers))))))) @@ -573,7 +598,7 @@ shape (get-in state (get-path state)) page-id (:current-page-id state) old-content (get-in state [:workspace-local :edit-path id :old-content]) - [rch uch] (generate-path-changes page-id id old-content (:content shape))] + [rch uch] (generate-path-changes page-id shape old-content (:content shape))] (rx/of (dwc/commit-changes rch uch {:commit-local? true})))))) (declare start-draw-mode) diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index dfeb88ddb..a62761cc0 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -431,6 +431,20 @@ (let [page-id (:current-page-id state)] (d/update-in-when state [:workspace-data :pages-index page-id :objects] set-rotation))))))) +(defn increase-rotation [ids rotation] + (ptk/reify ::increase-rotation + ptk/WatchEvent + (watch [_ state stream] + + (let [page-id (:current-page-id state) + objects (dwc/lookup-page-objects state page-id) + rotate-shape (fn [shape] + (let [delta (- rotation (:rotation shape))] + (set-rotation delta [shape])))] + (rx/concat + (rx/from (->> ids (map #(get objects %)) (map rotate-shape))) + (rx/of (apply-modifiers ids))))))) + (defn apply-modifiers [ids] (us/verify (s/coll-of uuid?) ids) diff --git a/frontend/src/app/main/ui/shapes/path.cljs b/frontend/src/app/main/ui/shapes/path.cljs index 90f485a0a..da40a1ef7 100644 --- a/frontend/src/app/main/ui/shapes/path.cljs +++ b/frontend/src/app/main/ui/shapes/path.cljs @@ -13,7 +13,6 @@ [rumext.alpha :as mf] [app.main.ui.shapes.attrs :as attrs] [app.main.ui.shapes.custom-stroke :refer [shape-custom-stroke]] - [app.common.geom.shapes :as geom] [app.util.object :as obj] [app.util.geom.path :as ugp])) @@ -24,14 +23,11 @@ [props] (let [shape (unchecked-get props "shape") background? (unchecked-get props "background?") - ;; {:keys [id x y width height]} (geom/shape->rect-shape shape) {:keys [id x y width height]} (:selrect shape) - transform (geom/transform-matrix shape) pdata (ugp/content->path (:content shape)) props (-> (attrs/extract-style-attrs shape) (obj/merge! - #js {:transform transform - :d pdata}))] + #js {:d pdata}))] (if background? [:g [:path {:stroke "transparent" diff --git a/frontend/src/app/main/ui/workspace/selection.cljs b/frontend/src/app/main/ui/workspace/selection.cljs index 4d53b6802..117f53a4b 100644 --- a/frontend/src/app/main/ui/workspace/selection.cljs +++ b/frontend/src/app/main/ui/workspace/selection.cljs @@ -44,16 +44,17 @@ (def selection-rect-width 1) (mf/defc selection-rect [{:keys [transform rect zoom color]}] - (let [{:keys [x y width height]} rect] - [:rect.main - {:x x - :y y - :width width - :height height - :transform transform - :style {:stroke color - :stroke-width (/ selection-rect-width zoom) - :fill "transparent"}}])) + (when rect + (let [{:keys [x y width height]} rect] + [:rect.main + {:x x + :y y + :width width + :height height + :transform transform + :style {:stroke color + :stroke-width (/ selection-rect-width zoom) + :fill "transparent"}}]))) (defn- handlers-for-selection [{:keys [x y width height]}] [;; TOP-LEFT @@ -181,9 +182,7 @@ current-transform (mf/deref refs/current-transform) selrect (:selrect shape) - transform (geom/transform-matrix shape {:no-flip true}) - - tr-shape (geom/transform-shape shape)] + transform (geom/transform-matrix shape {:no-flip true})] (when (not (#{:move :rotate} current-transform)) [:g.controls @@ -193,7 +192,7 @@ :transform transform :zoom zoom :color color}] - [:& outline {:shape tr-shape :color color}] + [:& outline {:shape shape :color color}] ;; Handlers (for [{:keys [type position props]} (handlers-for-selection selrect)] diff --git a/frontend/src/app/main/ui/workspace/shapes/bounding_box.cljs b/frontend/src/app/main/ui/workspace/shapes/bounding_box.cljs index dc3597a94..30f903fe3 100644 --- a/frontend/src/app/main/ui/workspace/shapes/bounding_box.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/bounding_box.cljs @@ -35,11 +35,12 @@ :stroke-width "1px" :stroke-opacity 0.5}]])) -(mf/defc render-rect [{{:keys [x y width height]} :rect :keys [color]}] +(mf/defc render-rect [{{:keys [x y width height]} :rect :keys [color transform]}] [:rect {:x x :y y :width width :height height + :transform (or transform "none") :style {:stroke color :fill "transparent" :stroke-width "1px" @@ -76,7 +77,7 @@ :fill line-color :stroke "white" :stroke-width 0.1} - (str/format "%s - (%s, %s)" (str/slice (str (:id shape)) 0 8) (fixed (:x shape)) (fixed (:y shape)))] + (str/format "%s - (%s, %s)" (str/slice (str (:id shape)) 0 8) (fixed (:x selrect)) (fixed (:y selrect)))] [:& cross-point {:point shape-center :zoom zoom @@ -87,7 +88,7 @@ :zoom zoom :color line-color}]) - [:& render-rect-points {:rect selrect + [:& render-rect-points {:points (:points shape) :color line-color}] [:& render-rect {:rect selrect diff --git a/frontend/src/app/main/ui/workspace/shapes/outline.cljs b/frontend/src/app/main/ui/workspace/shapes/outline.cljs index 309bdc5ff..102d761cf 100644 --- a/frontend/src/app/main/ui/workspace/shapes/outline.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/outline.cljs @@ -45,7 +45,8 @@ :ry (/ height 2)} :path - {:d (ugp/content->path (:content shape))} + {:d (ugp/content->path (:content shape)) + :transform nil} {:x x :y y