Repository navigation
Val calc support - #26409
Val calc support#26409ickshonpe wants to merge 47 commits into
Conversation
Remove CalcOps::Scale and push scales down to the leaves immediately, so repeated *= and /= no longer deepen the tree. Negative scales swap Min and Max, and turn Clamp into Min/Max to preserve the crossed bounds behavior. Replace stale Calc:: names with CalcOps::, rebuild the corrupted calc tests, and clone Vals in the crates that still relied on Val being Copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nit types when packing Vals.
Up to three terms with distinct units are stored inline as a `ValUnitSet` bitmask plus `[f32; 3]`, keeping `Val` at 16 bytes and making `px + percent`, `min` and `max` of leaves allocation-free. `+`, `-`, `min` and `max` fold same-unit terms, keep terms in unit order, and fall back to `Calc` for anything longer or nested. `Sum` equality ignores zero terms, `Min`/`Max` swap under negation, and all three interpolate term-wise. `LayoutCalc` now holds a `Val` so the layout path resolves them like `Calc`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Stores `[min, value, max]` as `[ValUnit; 3]` and `[f32; 3]`, keeping `Val` at 16 bytes. `Val::clamp` builds it when all three operands are unit values and falls back to `CalcOp::Clamp` otherwise. Negation swaps the bounds so it stays a `Clamp`, and it interpolates slot-wise. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Since they now take &self, the convention is to use a `to_` prefix.
`Val::to_physical` maps every leaf except percent to physical `Px` when the taffy style is rebuilt, folding through the `Val` operators so most expressions collapse to a single length or an inline sum of a length and a percentage. `LayoutCalc` then holds only that `Val` and resolves against taffy's basis alone, so it no longer stores a `LayoutContext`. `Val::clamp` now folds three same-unit operands. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Nothing in the layout pass reads it any more, since calc expressions are resolved to physical pixels when the taffy style is rebuilt. It is re-exported from `layout` so `bevy_ui::LayoutContext` is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`-clamp(lo, v, hi)` is `min(-lo, max(-v, -hi))`, which is the clamp with its bounds swapped only when `lo <= hi`. Same-unit bounds are ordered by construction, since inverted ones fold to the minimum, so they swap; mixed-unit bounds can invert at some bases, so they fall back to the exact `Calc` form as `CalcOp::scale` already does. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
serde only implements `Serialize` and `Deserialize` for `Arc<T>` behind its `rc` feature, which nothing in the graph enables, so the derived impls on `Calc` did not compile with the `serialize` feature. Delegating to the inner `CalcOp` fixes that and keeps the sharing out of the serialized form. Adds a ron round-trip test for expressions. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The swapped-bounds shortcut assumed same-unit bounds are ordered, which only holds for clamps built through `Val::clamp`. A `Val::Clamp` built directly with `min > max` resolves to its minimum, so negating it must give the negated minimum; check the ordering instead of assuming it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Moved the `Val` types and implementation from `geometry` into `val`.
Every calc pointer in a `TaffyStyle` points into one of the `Arc<PercentageCalc>`s stored alongside it, so `resolve_calc` can dereference the pointer taffy hands back. That removes the per-layout subtree walk, the pointer map and the `resolve_calc` closure plumbing. To keep that sound, `TaffyStyle` loses `DerefMut` and its fields become crate-private, so nothing outside the layout module can store a pointer in a style, and `MeasureArgs` is `#[non_exhaustive]` so it can only be built by the layout system. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Used it in `convert` to resolve leaf values.
… More doc comments and cleanup.
…to also use ValContext.
|
Hey from what I have seen from your current implementation is that you add Calc to the value enum. |
I think breaking changes are probably unavoidable. The question with a |
|
Yeah you are right. I would prefer like a The only non breaking version I can think of would be: promote Node to a special genericable struct (so that complexer val is opt in) So like But maybe indeed computable vals can be opt-in via generics, it would be a lot more rewritting requiring |
I thought about doing something similar to this in the past, but it was before required components. I think all the widgets that |
|
Another thing we could try is a feature gate, but I reckon it would be prohibitively complicated and fragile. Both internally and for 3rd party crates. |
|
You could always have an optional Also the require api could be updated to an |
Objective
Add support for more operations on
Vals:minandmaxclampSolution
There seems to be three main workable approaches but all of them have some drawbacks and compromises:
Calcvariant that wraps anArccontaining the expression tree. Expressions with up to three values can be packed into theValstruct.Calcvariant that wraps an id into some sort of global expression arena.With all three
Valhas to be non-copy. I think don't think copyable is viables, all the alternatives I considered give up composability and/or leak.This PR implements approach 1, using a 16-byte enum. I'm not convinced it's the best approach, but I'm not sure that there is a best approach. It seemed the simplest route for the initial PR, but then I got carried away with all the expression reduction and packing stuff.
Testing
Showcase
AI Disclosure
Used lots of AI