diff --git a/common/src/app/common/logic/libraries.cljc b/common/src/app/common/logic/libraries.cljc index 09683b4445..6468c86bc1 100644 --- a/common/src/app/common/logic/libraries.cljc +++ b/common/src/app/common/logic/libraries.cljc @@ -1401,26 +1401,23 @@ (dissoc :modifiers)))))))) (defn orient-like - "A function that turns each shape of the subtree of `head` around `center`, - so that `head` takes the flips and then the rotation of `target`; nil when - they already agree." + "Turn the subtree of `head` around `center` to match `target`'s matrix. + Flip flags can change during a rotation, so they cannot be applied as + separate transforms before that rotation." [head target center] - (let [flip-x? (not= (true? (:flip-x head)) (true? (:flip-x target))) - flip-y? (not= (true? (:flip-y head)) (true? (:flip-y target))) - flip (when (or flip-x? flip-y?) - (ctm/resize-modifiers (gpt/point (if flip-x? -1 1) (if flip-y? -1 1)) center)) - head' (cond-> head (some? flip) (gsh/transform-shape flip)) - angle (- (d/nilv (:rotation target) 0) (d/nilv (:rotation head') 0)) - rotate? (not (mth/close? (mod angle 360) 0 0.01)) - angle (if (> angle 180) (- angle 360) angle)] - (when (or (some? flip) rotate?) - (fn [shape] - (cond-> shape - (some? flip) - (gsh/transform-shape flip) - - rotate? - (as-> shape (gsh/transform-shape shape (ctm/rotation-modifiers shape center angle)))))))) + (when-not (and (= (:transform head) (:transform target)) + (mth/close? (or (:rotation head) 0) (or (:rotation target) 0) 0.01)) + (let [relative (gmt/multiply (or (:transform target) (gmt/matrix)) + (or (:transform-inverse head) (gmt/matrix))) + transform (gmt/transform-in center relative) + turned (gsh/apply-transform head transform) + angle (- (or (:rotation target) 0) (or (:rotation turned) 0))] + (when (or (not (gmt/unit? relative)) (not (mth/close? (mod angle 360) 0 0.01))) + (fn [shape] + (-> shape + (assoc-in [:modifiers :rotation] angle) + (gsh/apply-transform transform) + (dissoc :modifiers))))))) (defn- maybe-sync [c enabled? done? f] diff --git a/common/test/common_tests/logic/comp_expected_geometry_test.cljc b/common/test/common_tests/logic/comp_expected_geometry_test.cljc index 17bb6dac24..9a6f150a5d 100644 --- a/common/test/common_tests/logic/comp_expected_geometry_test.cljc +++ b/common/test/common_tests/logic/comp_expected_geometry_test.cljc @@ -11,6 +11,7 @@ through their constraints (decision (f) of the component contract)." (:require [app.common.files.changes-builder :as pcb] + [app.common.geom.matrix :as gmt] [app.common.geom.modifiers :as gm] [app.common.geom.shapes :as gsh] [app.common.logic.libraries :as cll] @@ -147,27 +148,29 @@ (t/deftest rotating-a-copy-with-text-in-a-detached-child-is-already-synced - (let [setup (fn [] - (tm/make-situation - (-> (thf/sample-file :file1) - (thtx/add-text-component-with-copy [[["Hello world"]]]) - (ths/update-shape :main-child :grow-type :auto-height) - (ths/update-shape :copy-child :grow-type :auto-height) - (ths/update-shape :copy-child :touched nil) - (tho/add-simple-component :outer :outer-root :outer-child - :root-params {:width 100 :height 100}) - (thc/instantiate-component :outer :outer-copy)))) - situation (r/run-variant - {:setup setup - :operation (tm/in-sequence [(n/nest-copy :copy-root :outer-root) - (n/detach :copy-root) - (n/rotate :outer-copy 45)])}) - file (tm/file situation) - text (first (filter #(= (thi/id :copy-child) (:shape-ref %)) - (vals (:objects (thf/current-page file))))) - full (:file (r/sync-cascade file {} (keys (get-in file [:data :components])))) - synced (ths/get-shape-by-id full (:id text))] - (t/is (some? text)) - (t/is (gsh/close-attrs? :points (:points text) (:points synced) 0.01)) - (t/is (mth/close? (:x text) (:x synced) 0.01)) - (t/is (mth/close? (:y text) (:y synced) 0.01)))) + (doseq [angle [45 135 225]] + (let [setup (fn [] + (tm/make-situation + (-> (thf/sample-file :file1) + (thtx/add-text-component-with-copy [[["Hello world"]]]) + (ths/update-shape :main-child :grow-type :auto-height) + (ths/update-shape :copy-child :grow-type :auto-height) + (ths/update-shape :copy-child :touched nil) + (tho/add-simple-component :outer :outer-root :outer-child + :root-params {:width 100 :height 100}) + (thc/instantiate-component :outer :outer-copy)))) + situation (r/run-variant + {:setup setup + :operation (tm/in-sequence [(n/nest-copy :copy-root :outer-root) + (n/detach :copy-root) + (n/rotate :outer-copy angle)])}) + file (tm/file situation) + text (first (filter #(= (thi/id :copy-child) (:shape-ref %)) + (vals (:objects (thf/current-page file))))) + full (:file (r/sync-cascade file {} (keys (get-in file [:data :components])))) + synced (ths/get-shape-by-id full (:id text))] + (t/is (some? text)) + (t/is (gmt/close? (:transform text) (:transform synced))) + (t/is (gsh/close-attrs? :points (:points text) (:points synced) 0.01)) + (t/is (mth/close? (:x text) (:x synced) 0.01)) + (t/is (mth/close? (:y text) (:y synced) 0.01)))))