Skip to content

fix: use bezier-easing 3.2 closed-form solver in CubicBezier - #145

Open
gre wants to merge 1 commit into
ReactUnity:mainfrom
gre:bezier-easing-3
Open

gre wants to merge 1 commit into
ReactUnity:mainfrom
gre:bezier-easing-3

Conversation

@gre

@gre gre commented Oct 8, 2026 •

Copy link
Copy Markdown

Hi, this is @gre, I'm the author of bezier-easing, which is also embedded in this project (TimingFunctions.CubicBezier in unity/core/Runtime/Styling/Animations/TimingFunctions.cs). The library was recently improved with better performance and more reliability, and I wanted to backport the benefits here.

This brings its 3.2 solver (closed-form, no sample table) to CSS cubic-bezier(...) transitions and animations:

  • Fixes steep curves. cubic-bezier(1, 0, 0, 1) at 0.4996 returned 0.348 instead of 0.431, so the transition went backwards for a frame.
  • Exact. It is computed in double and returned as float. On 138k float inputs, every result equals bezier-easing 3.2 rounded to float.
  • Lighter. No sample table is allocated per timing function, and the Newton/bisection helpers are gone. It uses Math.Cbrt, which is available from Unity 2021.3 (.NET Standard 2.1).
  • Values outside [0, 1] now saturate to start / end. The old search returned arbitrary values there. Linear curves are unchanged.

I checked it in a standalone .NET harness (with Mathf.Lerp stubbed) against the reference values and the old code. I couldn't run the Unity test suite locally.

download

Note

Medium Risk
Changes core animation easing math used by all transitions and preset eases; behavior improves for edge cases but any numerical differences could affect motion timing across the UI.

Overview
Replaces the iterative cubic-bezier solver in TimingFunctions.CubicBezier with the closed-form bezier-easing 3.2 approach, so CSS cubic-bezier(...) and built-in eases no longer use a per-curve sample table or Newton/bisection iteration.

Evaluation now solves for parameter t in double via solveTForX (Cardano / trigonometric cases with Math.Cbrt), then maps y(t) into the animation range. Inputs outside (0, 1) are handled by saturating through Linear instead of returning values from the old search. The linear shortcut when control points match the diagonal is unchanged.

This targets correctness on steep curves (e.g. cubic-bezier(1, 0, 0, 1) no longer briefly runs backward) and lower per-timing-function cost (no 11-sample allocation per Create call).

Reviewed by Cursor Bugbot for commit 74dec38. Bugbot is set up for automated code reviews on this repo. Configure here.

Replace the sample table + Newton-Raphson + bisection search (in float)
with the closed-form solver of bezier-easing 3.2, computed in double:
exact, and no backward jump on steep curves such as cubic-bezier(1, 0, 0, 1).

Values outside [0, 1] now saturate to start / end instead of going
through the old search, which returned arbitrary values there.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant