Put lengths, padding, gaps and alignment on the grid too
`Len` is `Px` beside `Rel` beside `Weight`, so the seam `4e28f10` left in `Span` -- a float length added to a fixed-point cursor -- is gone, and the sum a span compares against its box is exact. `Weight` is its own scale, `Fixed<16>`, because a share of what is left over is not a fraction of anything: a list divides its room by the total of them, so the range has to hold a whole list's worth while the precision only has to tell two weights apart. `Rel::ratio` turns two weights into a share on the finer grid, which is what a span needs and what dividing them on their own grid would round away. `AxisAlign` holds a `Rel` rather than a float, which is what the layout was reading out of it anyway. `Padding` and `Span::gap` hold `Px`, converted where they are built instead of on every frame. `RegionAlign::rel` is gone; its one caller wanted a position, and now builds one. `Fixed` gains `from_num` for a number as it is written in source, `mul_int` for a length repeated a whole number of times, and `ratio`. Checked: fmt, clippy, 101 tests, 100 generated seeds in 86 s, all five shrinker cases at 300 seeds, and all five examples byte-identical at 1920x1200 against `4e28f10`. With the fuzzer comparing for equality rather than within 0.05 px, four of the five cases now pass 100 seeds -- `resize-repaint` joins the other three. `reorder` still fails one seed by one step, so the last of it is in what a box is measured *in*: `px_len` and the window are still floats. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4e28f1047e
commit
bd6de71a55
15 files changed
+199
-158
No files matched your search
@@ -1,3 +1,4 @@
|
||||
use crate::UiNum;
|
||||
use std::{
|
||||
fmt::{Debug, Display, Formatter},
|
||||
ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign},
|
||||
@@ -28,6 +29,12 @@ pub struct Fixed<const SHIFT: u32>(i32);
|
||||
/// the same number reach the GPU.
|
||||
pub type Px = Fixed<6>;
|
||||
|
||||
/// A share of what a box has left over, which is a weight beside its
|
||||
/// siblings rather than a fraction of anything: a list divides its room by
|
||||
/// the total of these, so the range has to hold a whole list's worth and the
|
||||
/// precision only has to tell two weights apart.
|
||||
pub type Weight = Fixed<16>;
|
||||
|
||||
/// A fraction of a box. Twenty-four bits of it, which matches `f32` around a
|
||||
/// half and beats it above one -- where anchors actually sit -- and leaves
|
||||
/// +/-128 of range, enough to sum a hundred children each asking for a whole
|
||||
@@ -86,6 +93,12 @@ impl<const SHIFT: u32> Fixed<SHIFT> {
|
||||
})
|
||||
}
|
||||
|
||||
/// From a number as it is written in source -- `16`, `1.5` -- which is
|
||||
/// the other place a value enters the grid.
|
||||
pub fn from_num(v: impl UiNum) -> Self {
|
||||
Self::from_f32(v.to_f32())
|
||||
}
|
||||
|
||||
pub const fn to_f32(self) -> f32 {
|
||||
self.0 as f32 / Self::one().0 as f32
|
||||
}
|
||||
@@ -117,6 +130,11 @@ impl<const SHIFT: u32> Fixed<SHIFT> {
|
||||
Self(narrow(shift_round(self.0 as i64 * by.0 as i64, BY)))
|
||||
}
|
||||
|
||||
/// Repeated a whole number of times, which no grid rounds.
|
||||
pub const fn mul_int(self, by: i32) -> Self {
|
||||
Self(narrow(self.0 as i64 * by as i64))
|
||||
}
|
||||
|
||||
/// Divided by a number on any grid. A zero divisor is a caller bug -- a
|
||||
/// box of no length has no fraction of itself -- and saturates so that a
|
||||
/// release build lays out something absurd rather than dying.
|
||||
@@ -131,6 +149,16 @@ impl<const SHIFT: u32> Fixed<SHIFT> {
|
||||
Self(narrow(div_round((self.0 as i64) << BY, by.0 as i64)))
|
||||
}
|
||||
|
||||
/// `num / den` on *this* grid rather than on theirs, for weights coarser
|
||||
/// than the share they divide.
|
||||
pub const fn ratio<const OF: u32>(num: Fixed<OF>, den: Fixed<OF>) -> Self {
|
||||
debug_assert!(den.0 != 0, "no part of a whole of nothing");
|
||||
if den.0 == 0 {
|
||||
return Self::ZERO;
|
||||
}
|
||||
Self(narrow(div_round((num.0 as i64) << SHIFT, den.0 as i64)))
|
||||
}
|
||||
|
||||
/// `from` and `to` a fraction of the way apart, the fraction being the
|
||||
/// receiver -- the argument order [`crate::util::LerpUtil`] already uses.
|
||||
pub const fn lerp<const OF: u32>(self, from: Fixed<OF>, to: Fixed<OF>) -> Fixed<OF> {
|
||||
@@ -347,6 +375,15 @@ mod tests {
|
||||
assert_eq!(Rel::from_f32(0.5).lerp(to, from), Px::from_int(15));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_ratio_is_finer_than_the_weights_it_divides() {
|
||||
let (one, three) = (Weight::ONE, Weight::from_int(3));
|
||||
// A third, which the weights' own grid could only hold to 1/65536.
|
||||
assert_eq!(Rel::ratio(one, three), Rel::from_raw(5592405));
|
||||
assert_eq!(Rel::ratio(three, three), Rel::ONE);
|
||||
assert_eq!(Rel::ratio(Weight::ZERO, three), Rel::ZERO);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nothing_sits_between_a_value_and_the_next_one() {
|
||||
let at = Px::from_int(3);
|
||||
|
||||
Reference in new issue
Block a user