mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 05:50:35 +00:00
🐛 Settle swapped placement when updating a turned main
Updating a rotated copy also turns its main. A swapped descendant whose content and position sizes differ needs its placement settled in that new frame. Append the main-file placement changes to the inverse sync, keeping source-copy changes local and restoring both files in one undo group. Portable local/library regressions at 45 and 90 degrees fail eight assertions before the fix and pass all 20 after it. The original seed712 case4855 and its exact shrink pass, as does the 4855..5000 tail diagnostic. The diagnostic does not replace the full 5000-program swap gate. Common JVM1851/30684 and JS1782/29308, frontend1539/9312, backend1610/8104, exporter14/32 and all 68 plugin cases pass. Focused suites and unchanged goldens pass; lint and formatting pass. Live WASM at 45/90 degrees keeps placement stable through undo/redo and a later main edit. Maximum anchor error is below 0.00001px. Benchmark ratios against the recorded baseline: main 10/100/1000: 0.49/0.56/0.54; three levels: 0.49; chain: 0.49; library: 0.50; many shapes: 0.45; switch: 0.58. AI-assisted-by: GPT-6
This commit is contained in:
parent
a568b1d6cf
commit
2099084f0b
@ -78,3 +78,10 @@ Reset drops the root's geometry override from the whole sync context
|
||||
only while updating existing shapes can turn a newly added child by the
|
||||
old override after its parent has already reset. The reset and replacement
|
||||
children share one undo group and stay stable through later sync.
|
||||
|
||||
Update-main also settles untouched swapped heads carried through a parent
|
||||
turn in the updated main (F186). A rigid rotation can leave the wrong
|
||||
anchor when the replaced position and swap content have different sizes.
|
||||
Apply the main's nonlocal changes to its own file, settle changed placement,
|
||||
and append those changes to the same inverse-sync group. Keep the source
|
||||
copy's local changes routed separately, including for linked libraries.
|
||||
|
||||
@ -519,6 +519,24 @@
|
||||
(pcb/with-library-data library-data)
|
||||
(pcb/update-component id #(assoc % :path path :name name)))))
|
||||
|
||||
(defn- settle-updated-main-placement
|
||||
[changes libraries component-file component]
|
||||
(let [file (get libraries component-file)
|
||||
container (ctf/get-component-container (:data file) component)
|
||||
objects (:objects container)
|
||||
ids (filter #(ctk/swapped? (get objects %))
|
||||
(cfh/get-children-ids objects (:main-instance-id component)))]
|
||||
(if (seq ids)
|
||||
(let [redo (into [] (remove :local-change?) (:redo-changes changes))
|
||||
file (update file :data cfc/process-changes redo false)
|
||||
page (ctf/get-component-container (:data file) component)
|
||||
placement (-> (pcb/empty-changes)
|
||||
(pcb/with-container page)
|
||||
(pcb/with-objects (:objects page))
|
||||
(generate-sync-transformed-placement file libraries page objects ids))]
|
||||
(pcb/concat-changes changes placement))
|
||||
changes)))
|
||||
|
||||
(defn generate-sync-shape-inverse
|
||||
"Generate changes to update the component a shape is linked to, from
|
||||
the values in the shape and all its children."
|
||||
@ -545,7 +563,8 @@
|
||||
:synced-ids synced-ids}
|
||||
shape-inst
|
||||
(ctf/find-remote-shape container libraries shape-inst))
|
||||
(mark-local-changes @synced-ids)))
|
||||
(mark-local-changes @synced-ids)
|
||||
(settle-updated-main-placement libraries (:component-file shape-inst) component)))
|
||||
changes)))
|
||||
|
||||
(defn split-local-changes
|
||||
|
||||
@ -20,6 +20,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.files :as cf]
|
||||
[app.common.test-helpers.composable.comp.nodes :as n]
|
||||
[app.common.test-helpers.composable.comp.runner :as r]
|
||||
[app.common.test-helpers.composable.comp.setups :as setup]
|
||||
@ -36,6 +37,50 @@
|
||||
|
||||
(t/use-fixtures :each thi/test-fixture)
|
||||
|
||||
(t/deftest updating-a-turned-copy-settles-swapped-placement-in-the-main
|
||||
(doseq [remote? [false true]
|
||||
angle [45 90]]
|
||||
(let [file (-> (thf/sample-file :file)
|
||||
(tho/add-simple-component :a :main-a :rect-a
|
||||
:root-params {:width 100 :height 100})
|
||||
(tho/add-simple-component :other :main-other :rect-other
|
||||
:root-params {:width 100 :height 100})
|
||||
(tho/add-frame :main-b {:width 200 :height 200})
|
||||
(thc/instantiate-component :a :b-a :parent-label :main-b)
|
||||
(thc/make-component :b :main-b)
|
||||
(tho/add-frame :main-c {:width 300 :height 300})
|
||||
(thc/instantiate-component :b :c-b :parent-label :main-c
|
||||
:children-labels [:c-a])
|
||||
(thc/make-component :c :main-c))
|
||||
prepared (-> (tm/make-situation file)
|
||||
(r/step (n/change-height :b-a 80)) r/settle
|
||||
(r/step (n/swap-shape :c-a :other)) r/settle)
|
||||
library (tm/file prepared)
|
||||
initial (if remote?
|
||||
(-> (tm/make-situation
|
||||
(thc/instantiate-component (thf/sample-file :local) :c :copy-c
|
||||
:library library :children-labels [:copy-b :copy-a]))
|
||||
(tm/with-aux-files {(:id library) library}))
|
||||
(tm/with-file prepared
|
||||
(thc/instantiate-component library :c :copy-c
|
||||
:children-labels [:copy-b :copy-a])))
|
||||
before (-> initial (r/step (n/rotate :copy-c angle)) r/settle)
|
||||
updated (-> before (r/step (n/update-main :copy-c)) r/settle)
|
||||
main-file (if remote? (get (tm/aux-files updated) (:id library)) (tm/file updated))
|
||||
head (ths/get-shape main-file :c-a)
|
||||
full (:situation (cf/sync-world updated (keys (get-in main-file [:data :components]))))
|
||||
full-main (if remote? (get (tm/aux-files full) (:id library)) (tm/file full))
|
||||
full-head (ths/get-shape-by-id full-main (:id head))]
|
||||
(t/is (mth/close? angle (:rotation head) 0.001))
|
||||
(t/is (= #{:swap-group} (:touched head)))
|
||||
(t/is (gsh/close-attrs? :points (:points full-head) (:points head) 0.001))
|
||||
(t/is (gsh/close-attrs? :points
|
||||
(:points (tm/resolve-shape full :copy-a))
|
||||
(:points (tm/resolve-shape updated :copy-a)) 0.001))
|
||||
(t/is (= (into {} (map (fn [[id f]] [id (thn/normalize-file f)])) (cf/all-files before))
|
||||
(into {} (map (fn [[id f]] [id (thn/normalize-file f)]))
|
||||
(cf/all-files (r/step updated (n/undo)))))))))
|
||||
|
||||
(t/deftest resetting-a-turned-copy-places-replacement-children-in-the-main-frame
|
||||
(doseq [angle [45 90]]
|
||||
(let [file (-> (thf/sample-file :file)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user