Four rounds over the same idea: an expression that needed a comment to say
what it computed wanted to be a named operation.
The placement description is built by chaining off the value that says it.
`UiSpan::within_desc`/`shifted_desc` and `Len::as_desc` replace the
`PlaceDescAxis::` constructors, `PlaceDescAxis::axis` lifts one axis into a
pair with the whole box across it, and `PlaceDesc::per_axis` covers the case
where the two axes differ. `beside` is dropped: `from_axis` already said it.
Seven module-level functions become methods on the value each took first --
`Widgets::declared_lens`, `LayoutLen::fills`, `PlaceDesc::placement` and
`::rel_base_and_region`, `Size::within_box`, `UiRegion::at_origin` and
`::as_translation`.
`UiSpan::place` is the aligned-placement rule, which was written out three
times; `LayoutLen::without_leftover` is the sibling `apply_leftover` never
had, at six sites; `is_px` and `is_only_leftover` name field comparisons the
surrounding comments had to translate; `Holds::covers` was interval
containment spelled out by hand. A span's `shared` loses the two arguments
that did not vary across its loop.
`LayoutHolds` was four two-element arrays where every other pair here is a
struct of two per-axis values, so nothing it did could be written once.
It becomes `AxisHolds` on `x` and `y`, and `and`, `covers` and `contains`
lose their loops.
Every pair gets `Index<Axis>`/`IndexMut<Axis>` through one macro, and the
eighteen `axis`/`axis_mut` methods go. `const_index` keeps the accessors
usable in const context.
Cold layout is unchanged: `layout_dump` over 400 depth-5 trees is identical
to 58ce74d byte for byte, across all 34,492 boxes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
124 lines
3.9 KiB
Rust
124 lines
3.9 KiB
Rust
pub const trait LerpUtil {
|
|
fn lerp(self, from: Self, to: Self) -> Self;
|
|
}
|
|
|
|
const impl LerpUtil for f32 {
|
|
/// linear interpolation
|
|
/// from * (1.0 - self) + to * self
|
|
fn lerp(self, from: Self, to: Self) -> Self {
|
|
from + (to - from) * self
|
|
}
|
|
}
|
|
|
|
macro_rules! impl_op {
|
|
($T:ident $op:ident $fn:ident $opa:ident $fna:ident; $($field:ident)*) => {
|
|
#[allow(non_snake_case)]
|
|
mod ${concat($T, _op_, $fn, _impl)} {
|
|
use super::*;
|
|
#[allow(unused_imports)]
|
|
use std::ops::*;
|
|
const impl $op for $T {
|
|
type Output = Self;
|
|
|
|
fn $fn(self, rhs: Self) -> Self::Output {
|
|
Self {
|
|
$($field: self.$field.$fn(rhs.$field),)*
|
|
}
|
|
}
|
|
}
|
|
const impl $opa for $T {
|
|
fn $fna(&mut self, rhs: Self) {
|
|
*self = self.$fn(rhs);
|
|
}
|
|
}
|
|
const impl $op<f32> for $T {
|
|
type Output = Self;
|
|
|
|
fn $fn(self, rhs: f32) -> Self::Output {
|
|
Self {
|
|
$($field: self.$field.$fn(rhs),)*
|
|
}
|
|
}
|
|
}
|
|
const impl $op<$T> for f32 {
|
|
type Output = $T;
|
|
|
|
fn $fn(self, rhs: $T) -> Self::Output {
|
|
$T {
|
|
$($field: self.$fn(rhs.$field),)*
|
|
}
|
|
}
|
|
}
|
|
const impl $opa<f32> for $T {
|
|
fn $fna(&mut self, rhs: f32) {
|
|
*self = self.$fn(rhs);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
// Without the `f32` operations, for a type whose fields are not all the
|
|
// same kind of number: there is nothing a bare float means to a fraction
|
|
// and an offset at once.
|
|
(same $T:ident $op:ident $fn:ident $opa:ident $fna:ident; $($field:ident)*) => {
|
|
#[allow(non_snake_case)]
|
|
mod ${concat($T, _op_, $fn, _same_impl)} {
|
|
use super::*;
|
|
#[allow(unused_imports)]
|
|
use std::ops::*;
|
|
const impl $op for $T {
|
|
type Output = Self;
|
|
|
|
fn $fn(self, rhs: Self) -> Self::Output {
|
|
Self {
|
|
$($field: self.$field.$fn(rhs.$field),)*
|
|
}
|
|
}
|
|
}
|
|
const impl $opa for $T {
|
|
fn $fna(&mut self, rhs: Self) {
|
|
*self = self.$fn(rhs);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
(same $T:ident $op:ident $fn:ident; $($field:ident)*) => {
|
|
impl_op!(same $T $op $fn ${concat($op,Assign)} ${concat($fn,_assign)}; $($field)*);
|
|
};
|
|
($T:ident $op:ident $fn:ident; $($field:ident)*) => {
|
|
impl_op!($T $op $fn ${concat($op,Assign)} ${concat($fn,_assign)}; $($field)*);
|
|
};
|
|
(impl $op:ident for $T:ident: $fn:ident $($field:ident)*) => {
|
|
impl_op!($T $op $fn ${concat($op,Assign)} ${concat($fn,_assign)}; $($field)*);
|
|
};
|
|
}
|
|
|
|
pub(crate) use impl_op;
|
|
|
|
/// `Index<Axis>` for a pair, which is how every pair here is read by axis.
|
|
/// The generics clause is given in braces where the type has one.
|
|
macro_rules! impl_axis_index {
|
|
($({$($gen:tt)*})? $T:ty => $Out:ty) => {
|
|
const impl $(<$($gen)*>)? std::ops::Index<crate::Axis> for $T {
|
|
type Output = $Out;
|
|
|
|
fn index(&self, axis: crate::Axis) -> &$Out {
|
|
match axis {
|
|
crate::Axis::X => &self.x,
|
|
crate::Axis::Y => &self.y,
|
|
}
|
|
}
|
|
}
|
|
|
|
const impl $(<$($gen)*>)? std::ops::IndexMut<crate::Axis> for $T {
|
|
fn index_mut(&mut self, axis: crate::Axis) -> &mut $Out {
|
|
match axis {
|
|
crate::Axis::X => &mut self.x,
|
|
crate::Axis::Y => &mut self.y,
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
pub(crate) use impl_axis_index;
|