🐛 Default absent layout padding to zero

Stored layout frames may omit padding. The padding helpers returned nil
sides, so padding totals and full flex/grid layout calculation crashed on
the JVM. Default absent padding to zero and share that reader between
horizontal and vertical totals.

Add portable regressions that prove the inputs pass the shape schema,
then compare their layout calculation with explicit zero padding. Both
regressions fail before the fix; existing simple/multiple checks stay green.

Validate common JVM (1997 tests) and JS (1933), frontend (1742), backend
(1793) and exporter (16), plus lint and formatting for all four modules.
All six generated properties pass 40 programs at seed 712 in both runtimes.
Golden data is unchanged.

The benchmark passes with no regression flags against the existing baseline.
Lower-quantile ratios: main 10/100/1000 copies 0.51/0.58/0.60, three levels
0.56, chain 0.52, library 0.53, many shapes 0.43, variant switch 0.64.
The tool warns that machine/JVM differences make these rough comparisons.
No performance baseline changed.

AI-assisted-by: gpt-6
This commit is contained in:
alonso.torres 2026-10-28 18:11:40 +01:00
parent 02640e745c
commit 78feb03b83
3 changed files with 41 additions and 11 deletions

View File

@ -328,24 +328,21 @@
(defn paddings
[{:keys [layout-padding-type layout-padding]}]
(let [{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding]
(let [{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4
:or {pad-top 0 pad-right 0 pad-bottom 0 pad-left 0}} layout-padding]
(if (= :simple layout-padding-type)
[pad-top pad-right pad-top pad-right]
[pad-top pad-right pad-bottom pad-left])))
(defn h-padding
[{:keys [layout-padding-type layout-padding]}]
(let [{pad-right :p2 pad-left :p4} layout-padding]
(if (= :simple layout-padding-type)
(+ pad-right pad-right)
(+ pad-right pad-left))))
[shape]
(let [[_ right _ left] (paddings shape)]
(+ right left)))
(defn v-padding
[{:keys [layout-padding-type layout-padding]}]
(let [{pad-top :p1 pad-bottom :p3} layout-padding]
(if (= :simple layout-padding-type)
(+ pad-top pad-top)
(+ pad-top pad-bottom))))
[shape]
(let [[top _ bottom _] (paddings shape)]
(+ top bottom)))
(defn padding-type-for
"`:simple` when top≈bottom and left≈right, `:multiple` otherwise (nil sides = 0)."

View File

@ -11,7 +11,9 @@
[app.common.geom.point :as gpt]
[app.common.geom.rect :as grc]
[app.common.geom.shapes.flex-layout.bounds :as fb]
[app.common.geom.shapes.flex-layout.layout-data :as fld]
[app.common.geom.shapes.grid-layout.bounds :as gb]
[app.common.geom.shapes.grid-layout.layout-data :as gld]
[app.common.geom.shapes.min-size-layout :as msl]
[app.common.types.shape :as cts]
[app.common.types.shape.layout :as ctl]
@ -89,6 +91,27 @@
(apply dissoc full exclude-ids)
full)))
(t/deftest layout-calculation-without-optional-padding
(doseq [layout-type [:flex :grid]
padding-type [nil :simple :multiple]]
(let [parent-id (uuid/next)
child-id (uuid/next)
child (make-rect child-id 10 10 50 50)
parent (cond-> (dissoc (case layout-type
:flex (make-flex-frame parent-id [child-id])
:grid (make-grid-frame parent-id [child-id]))
:layout-padding :layout-padding-type)
padding-type (assoc :layout-padding-type padding-type))
objects (make-objects [parent child])
bounds (bounds-map-from-objects objects)
calculate (case layout-type :flex fld/calc-layout-data :grid gld/calc-layout-data)
explicit (assoc parent :layout-padding {:p1 0 :p2 0 :p3 0 :p4 0})]
(t/is (cts/valid-shape? parent))
(t/is (= (calculate explicit (:points parent) [[(:points child) child]] bounds
(assoc objects parent-id explicit))
(calculate parent (:points parent) [[(:points child) child]] bounds objects))
(str "Absent padding must keep " layout-type " layout geometry")))))
;; ---- Tests for flex layout bounds with nil bounds ----
(t/deftest layout-content-points-with-missing-parent-bounds

View File

@ -354,6 +354,16 @@
:layout-padding {:p1 8 :p2 10 :p3 99 :p4 99})]
(t/is (= 16 (layout/v-padding shape))))))
(t/deftest absent-layout-padding-means-zero
(doseq [layout-type [:flex :grid]
padding-type [nil :simple :multiple]]
(let [shape (cond-> (make-frame :layout layout-type)
padding-type (assoc :layout-padding-type padding-type))]
(t/is (cts/valid-shape? shape) "Padding is optional in stored shapes")
(t/is (= [0 0 0 0] (layout/paddings shape)))
(t/is (zero? (layout/h-padding shape)))
(t/is (zero? (layout/v-padding shape))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; child-min/max-width/height
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;