Record the doubled declared fraction, and what a squeezed span does

The text reference render is not byte-identical after all: a declared
rel or rest length is applied by the span and then again inside SetSize,
so .width(rel(0.5)) draws its child at a quarter. Also replaces the
example that measured one-pixel lines with what the tests now say, since
a span short of room overlaps its fixed lengths rather than collapsing
them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
iris-aiandClaude Opus 5 committed 2026-09-15 12:32:12 -04:00
1 parent 89d20c31dc
commit e6b9cb29b7
1 file changed
+32 -8
+32 -8
View File
@@ -26,6 +26,22 @@ reference renders and the resize render are byte-identical across all of
it, and the hundred-seed sweep passes. `/home/bob/repos/ai-app-2` is on it, and the hundred-seed sweep passes. `/home/bob/repos/ai-app-2` is on
`rustify`, worktree clean. `rustify`, worktree clean.
**A declared fraction is applied twice, and it is the one thing to fix before
#18 lands.** `.width(rel(0.5))` in a 400-wide span puts the `SetSize` at 0..200
and its child at 0..100: the span sizes the box from the hint, and `c596bf1`
then measures the child in the declared length again *inside* that box. A
declared pixel length is unaffected -- 200 px of a box already 200 px wide is
the whole box -- so only `rel` and `rest` double. It shows in the `text`
example, whose "same words in half the width" panel is 474 px where
`upstream/main` draws 948, and that is 111,923 pixels of difference, so **the
claim above that the five reference renders are byte-identical is wrong for
`text`**; `tabs`, `view` and `minimal` are identical. The structural fix is the
built-in size already queued below -- a declared length resolved once, by the
parent -- rather than a second rule inside `SetSize`, which cannot tell whether
its box is its own declared length or the whole of what its parent had.
Minimal repro: `(inner.width(rel(0.5)), rect()).span(Dir::RIGHT)` at 400 wide,
asking where `inner` landed.
**The `tabs` divergence does not reproduce, so nothing waits on the owner.** **The `tabs` divergence does not reproduce, so nothing waits on the owner.**
Re-measured 2026-09-15 at `f61e893` against `upstream/main` built in its own Re-measured 2026-09-15 at `f61e893` against `upstream/main` built in its own
worktree: `tabs` at 1920x1200 is byte-identical, and so are a cold 900x1200 and worktree: `tabs` at 1920x1200 is byte-identical, and so are a cold 900x1200 and
@@ -35,6 +51,12 @@ and x=1337 is withdrawn; it was taken before `02ff8c7` and `f61e893`, and a
target directory shared between the two checkouts is the trap the render target directory shared between the two checkouts is the trap the render
section already warns about. Byte-identical against `upstream/main` is met. section already warns about. Byte-identical against `upstream/main` is met.
`4063635` adds the two hairline tests to `tests/layout.rs`, and `169db7f`
writes the shader's `scalar_within` the way `UiScalar::within` writes it --
`from + (to - from) * t` rather than `mix` -- so the CPU and the GPU compose a
position with the same arithmetic and one multiplication fewer. All five
renders are unchanged by it.
Check for a review before starting anything, and read the newest `submitted_at` Check for a review before starting anything, and read the newest `submitted_at`
rather than the first result: rather than the first result:
@@ -105,14 +127,16 @@ Invariants, not history. Everything in `core/src/ui` rests on them.
gap can therefore move by a pixel when a position falls the other side of the gap can therefore move by a pixel when a position falls the other side of the
`floor`; it cannot be widened or deleted by one. Only a length expressed as a `floor`; it cannot be widened or deleted by one. Only a length expressed as a
fraction can round away, and that was as true before the chain. Measured fraction can round away, and that was as true before the chain. Measured
2026-09-15 with `examples/thin.rs` (untracked in the #18 worktree): sixteen 2026-09-15 by `tests/layout.rs`, which composes sixteen hairlines under four
one-pixel lines and sixteen one-pixel gaps under four levels of span, padding levels of span and padding on `rest(3)/rest(7)/rest(5)` weights and applies
and `rest(3)/rest(7)/rest(5)`, rendered at six output sizes on the shader's own snapping at four output sizes. **A span short of room takes
`upstream/main` and at `f61e893`. Every run is exactly one pixel on both, and it from its shares and never from a fixed length**: the shares reach zero and
a line sits within one pixel of where the other revision put it. At 640x481 then go past it, so the fixed parts start overlapping each other -- at 400
the fixture is over-constrained -- the fixed pixels exceed the box -- and the wide three marks sit at 132, 265 and 399, at 3 wide at 0, 1 and 2, and at 1
same three gaps vanish on both revisions, which is the span overflowing wide two of them land on the same pixel. Overlapping is what hides a
rather than anything rounding. separator in an over-constrained tree; collapsing one never happens. The same
line written as `rel(1.0 / 1920.0)` instead is zero pixels wide even at 1920,
which is the mutation that shows the tests have teeth.
- **The walk stops where a length did not change.** A part of a box with no - **The walk stops where a length did not change.** A part of a box with no
relative extent on an axis is a fixed length held as offsets from that box's relative extent on an axis is a fixed length held as offsets from that box's
start, and composing anything into it leaves no relative extent either -- so a start, and composing anything into it leaves no relative extent either -- so a