mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 14:00:37 +00:00
🐛 Ignore dot product roundoff when detecting shape flips
A right-angle turn can produce a tiny negative dot product. Compare it relative to the axis lengths before treating the turn as a reflection. Red regression reproduced at small, normal and large sizes. Focused geometry, reset, swap and runner suites pass 121 tests / 540 assertions. Benchmark vs Phase 1e: main 10/100/1000 0.51/0.64/0.65; levels 0.50; chain 0.57; library 0.53; many shapes 0.47; switch 0.59. No flags. AI-assisted-by: GPT-6
This commit is contained in:
parent
a90c6d1d8c
commit
835138749b
@ -297,19 +297,26 @@
|
||||
|
||||
yv1 (gpt/to-vec p0' (nth points' 3))
|
||||
yv2 (gpt/to-vec p0 (nth points 3))
|
||||
dot-y (gpt/dot yv1 yv2)]
|
||||
dot-y (gpt/dot yv1 yv2)
|
||||
|
||||
;; A right-angle turn has a zero dot product. Coordinate roundoff
|
||||
;; must not turn that into a reflection, regardless of shape size.
|
||||
flip-x? (and (neg? dot-x)
|
||||
(not (mth/close? dot-x 0 (* 1e-10 (gpt/length xv1) (gpt/length xv2)))))
|
||||
flip-y? (and (neg? dot-y)
|
||||
(not (mth/close? dot-y 0 (* 1e-10 (gpt/length yv1) (gpt/length yv2)))))]
|
||||
|
||||
(cond-> shape
|
||||
(neg? dot-x)
|
||||
flip-x?
|
||||
(update :flip-x not)
|
||||
|
||||
(neg? dot-y)
|
||||
flip-y?
|
||||
(update :flip-y not)
|
||||
|
||||
;; Negate rotation only when an odd number of axes are flipped,
|
||||
;; since flipping both axes is equivalent to a 180° rotation and
|
||||
;; two negations would cancel each other out.
|
||||
(not= (neg? dot-x) (neg? dot-y))
|
||||
(not= flip-x? flip-y?)
|
||||
(update :rotation -))))
|
||||
|
||||
(defn- apply-transform-move
|
||||
|
||||
@ -271,3 +271,14 @@
|
||||
(t/is (true? (:flip-y result)))
|
||||
;; rotation must not be negated when both axes are flipped
|
||||
(t/is (mth/close? 30 (:rotation result))))))
|
||||
|
||||
(t/deftest a-right-angle-turn-does-not-invent-a-flip-from-roundoff
|
||||
(doseq [size [0.01 100 1000000]]
|
||||
(let [shape (create-test-shape :rect {:width size :height size :rotation 90})
|
||||
shape (assoc shape :points [(gpt/point size 0) (gpt/point size size)
|
||||
(gpt/point 0 size) (gpt/point 0 (* size 1e-14))])
|
||||
turn (gmt/transform-in (gpt/point (/ size 2) (/ size 2)) (gmt/rotate-matrix 90))
|
||||
after (gsh/apply-transform (assoc-in shape [:modifiers :rotation] 90) turn)]
|
||||
(t/is (not (:flip-x after)))
|
||||
(t/is (not (:flip-y after)))
|
||||
(t/is (mth/close? 180 (:rotation after))))))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user