🐛 Preserve resize overrides across rotated variant switches

A clean target can acquire tiny size drift while taking the old copy's
rotation. Exact main-size comparisons then discard a valid resize.
Compare geometry with its float tolerance, including fixed layout sizes
and composite bounds; keep nongeometry values exact.

The portable rotation/resize regression fails both height and selrect
checks before the fix. Cover ordinary and fixed layout sizing, and
check child placement from the original library shrink separately.
The full variant suite passes on JVM and JS: 56 tests / 337 assertions.

Benchmark lower-quantile ratios against the Phase 1e baseline:
main 10/100/1000 copies 0.52/0.60/0.60; three levels 0.51;
chain 0.52; library 0.54; many shapes 0.46; variant switch 0.65.
No slowdown flags.

AI-assisted-by: GPT-6
This commit is contained in:
alonso.torres 2026-10-24 01:23:38 +02:00
parent d9c2be1994
commit 8a2c2b7246
3 changed files with 36 additions and 5 deletions

View File

@ -39,4 +39,12 @@ The generic fallback branch copies from `previous-shape`. It represents the inte
`common/src/app/common/test_helpers/compositions.cljc` has `swap-component-in-shape`, which drives `generate-component-swap` plus `generate-keep-touched` with the production `keep-touched?` flag. Use it for focused common tests of variant-switch behavior.
`common/test/common_tests/logic/variants_switch_test.cljc` is the canonical reference suite for swap+touched scenarios. Read nearby tests before adding another case.
`common/test/common_tests/logic/variants_switch_test.cljc` is the canonical reference suite for swap+touched scenarios. Read nearby tests before adding another case.
## Variant geometry comparison
Switch carry compares the two mains' geometry with `gsh/close-attrs?`.
The clean target already has the copy's rotation, which can turn 100 into
99.99999999999994. Exact dimension equality drops valid resize overrides
(F160). Fixed layout sizes and composite geometry use the same tolerance;
nongeometry scalar attributes still compare exactly.

View File

@ -100,7 +100,7 @@
(let [prev (-> prev-shape :selrect dimension)
current (-> current-shape :selrect dimension)
origin (-> origin-shape :selrect dimension)]
(if (= origin current) prev current)))
(if (mth/close? origin current) prev current)))
width (size :width)
height (size :height)]
(case attr
@ -204,7 +204,9 @@
;; 5. The bg of Copy now is blue (we ignore the override)
(and
(not (contains? #{:points :selrect :content} attr))
(not= (get origin-ref-shape attr) (get current-shape attr)))
(not (if (= sync-group :geometry-group)
(gsh/close-attrs? attr (get origin-ref-shape attr) (get current-shape attr))
(= (get origin-ref-shape attr) (get current-shape attr)))))
;; The :content attr cant't be copied to elements of different type
(and (= attr :content) (not= (:type previous-shape) (:type current-shape))))
@ -250,8 +252,8 @@
(not path-change?)
(not (or (= :fix (:layout-item-h-sizing previous-shape))
(= :fix (:layout-item-v-sizing previous-shape))))
(or (not= (get origin-ref-shape :width) (get current-shape :width))
(not= (get origin-ref-shape :height) (get current-shape :height)))))
(or (not (mth/close? (:width origin-ref-shape) (:width current-shape)))
(not (mth/close? (:height origin-ref-shape) (:height current-shape))))))
attr-val
(when-not skip?

View File

@ -15,6 +15,7 @@
[app.common.logic.shapes :as cls]
[app.common.math :as mth]
[app.common.test-helpers.components :as thc]
[app.common.test-helpers.composable.comp.nodes :as n]
[app.common.test-helpers.compositions :as tho]
[app.common.test-helpers.files :as thf]
[app.common.test-helpers.ids-map :as thi]
@ -28,6 +29,26 @@
(t/use-fixtures :each thi/test-fixture)
(t/deftest switch-keeps-a-resize-override-after-rotating-the-copy
(doseq [sizing [nil :fix]]
(let [params {:width 100 :height 100 :layout-item-v-sizing sizing}
file (-> (thf/sample-file :file1)
(thv/add-variant :v01 :c01 :m01 :c02 :m02
{:variant1-params params :variant2-params params})
(thc/instantiate-component :c01 :copy01))
file (reduce (fn [file modifiers]
(let [shape (ths/get-shape file :copy01)]
(thf/apply-changes file
(n/transform-changes (thf/current-page file) shape
(modifiers shape)))))
file [#(ctm/rotation-modifiers % (gsh/shape->center %) 45)
#(ctm/change-size % nil 80)])
switched (tho/swap-component-in-shape file :copy01 :c02 {:keep-touched? true})
copy (ths/get-shape switched :copy01)]
(t/is (mth/close? 80 (:height copy) 1e-10))
(t/is (mth/close? 80 (get-in copy [:selrect :height]) 1e-10))
(t/is (contains? (:touched copy) :geometry-group)))))
;; ============================================================
;; BASIC SWITCH TESTS (no overrides)
;; ============================================================