🐛 Bound flex free-space distribution

Cross-axis fill sizing reaches distribution before the current line size
is initialized. Count eligible items from the minimum fallback, as the
update loop already does. Use zero increments when every item is at its
maximum, avoiding division by zero.

Add portable controls for minimum initialization, redistribution at a cap
and fully saturated items. All three raise arithmetic errors before the
fix and pass afterward. Generated vertical-fill histories cover the full
layout and component pipeline too.

Validate the final continuation on common JVM (2004 tests / 32919
assertions), common JS (1940 / 31522), frontend (1745 / 13569), backend
(1793 / 9007) and exporter (16 / 40). All four modules pass lint and format.
All six generated properties pass 40 programs at seed 712 in both runtimes.
Golden data is unchanged.

The final continuation benchmark passes with no regression flags. Lower
quantile ratios against Phase 1e: main 10/100/1000 0.51/0.58/0.57, three
levels 0.57, chain 0.53, library 0.57, many shapes 0.45, variant switch 0.66.
Machine/JVM differences make these rough comparisons; baseline unchanged.

AI-assisted-by: gpt-6
This commit is contained in:
alonso.torres 2026-10-28 19:57:27 +01:00
parent b11e91f6e1
commit 96a4c76ab6
2 changed files with 19 additions and 2 deletions

View File

@ -142,8 +142,10 @@
(defn add-space-to-items
;; Distributes the remainder space between the lines
[prop prop-min prop-max to-share items]
(let [num-items (->> items (remove #(mth/close? (get % prop) (get % prop-max))) count)
per-line-target (/ to-share num-items)]
(let [num-items (->> items
(remove #(mth/close? (or (get % prop) (get % prop-min) 0) (get % prop-max)))
count)
per-line-target (if (pos? num-items) (/ to-share num-items) 0)]
(loop [items (seq items)
remainder to-share

View File

@ -176,3 +176,18 @@
{(:id frame) bounds (:id child) child-bounds} objects false)]
(t/is (= 1 (count lines)))
(t/is (= 1 (:num-children (first lines)))))))
(t/deftest flex-space-distribution-starts-at-the-minimum-and-stops-at-the-maximum
(t/is (= [{:min-size 20 :max-size 30 :size 30}
{:min-size 10 :max-size 100 :size 40}]
(fld/distribute-space :size :min-size :max-size 30 70
[{:min-size 20 :max-size 30}
{:min-size 10 :max-size 100}])))
(t/is (= [{:min-size 20 :max-size 30 :size 30}
{:min-size 10 :max-size 40 :size 40}]
(fld/distribute-space :size :min-size :max-size 30 100
[{:min-size 20 :max-size 30}
{:min-size 10 :max-size 40}])))
(t/is (= [[{:min-size 10 :max-size 10 :size 10}] 5]
(fld/add-space-to-items :size :min-size :max-size 5
[{:min-size 10 :max-size 10 :size 10}]))))