an.stage.easing#

The stage engine’s easing vocabulary — a view of an.timing.easing.

The curves, their solvers and the full registry live in the timing kernel (an.timing.easing). This module keeps the path every stage caller already imports, and it states what the STAGE implements: runtime.js evaluates the legacy names in an.base.EASING_PRESETS and a cubic-Bézier control 4-tuple, nothing else, so apply_easing() here refuses any other name. That refusal is what an validate and the compiler use to say “the evaluators know this easing” — a curve the kernel knows but the stage runtime does not would otherwise surface as a throw in the browser.

>>> apply_easing("linear", 0.5)
0.5
>>> round(apply_easing("ease_in_out", 0.5), 6)
0.5
>>> round(apply_easing([0.0, 0.0, 1.0, 1.0], 0.5), 6)
0.5
>>> apply_easing("ease-in", 0.5)
Traceback (most recent call last):
 ...
an.timing.easing.UnknownEasingError: unknown easing preset 'ease-in'; known: ['ease', 'ease_in', 'ease_in_out', 'ease_out', 'linear', 'step']

Module Attributes

EASING_FUNCS

The easings the stage runtime implements (runtime.js EASINGS), each the kernel registry's own curve.

Functions

apply_easing(spec, t)

Apply an easing the STAGE implements to t in [0, 1].

cubic_bezier(cx1, cy1, cx2, cy2, t)

an's cubic-Bézier easing at t — the an-bezier-newton-8 solver.

an.stage.easing.EASING_FUNCS: dict[str, Callable[[float], float]] = {'ease': <function _ease>, 'ease_in': <function _ease_in>, 'ease_in_out': <function _ease_in_out>, 'ease_out': <function _ease_out>, 'linear': <function _linear>, 'step': <function _step>}#

The easings the stage runtime implements (runtime.js EASINGS), each the kernel registry’s own curve.

an.stage.easing.apply_easing(spec, t)[source]#

Apply an easing the STAGE implements to t in [0, 1].

  • None → linear (passthrough)

  • a string preset name (must be a key of EASING_FUNCS)

  • a 4-element sequence of cubic-Bézier control points (the legacy solver)

Raises ValueError for unknown preset names or malformed sequences.

>>> apply_easing(None, 0.25)
0.25
>>> apply_easing("step", 0.99)
0.0
>>> apply_easing("step", 1.0)
1.0
Return type:

float

an.stage.easing.cubic_bezier(cx1, cy1, cx2, cy2, t)#

an’s cubic-Bézier easing at t — the an-bezier-newton-8 solver.

The curve is defined by P0=(0,0), P1=(cx1,cy1), P2=(cx2,cy2), P3=(1,1). Given a desired x=t we solve for the matching curve parameter u, then return the y coordinate, by exactly 8 clamped Newton steps.

Structurally IDENTICAL to runtime.js::cubicBezier on purpose: always 8 iterations, break only on a degenerate derivative, clamp each step. This function is the spec of that port, and the two are compared bit-for-bit by the parity battery — an earlier version had an extra |u_new - u| < 1e-9 early-convergence break the JS side lacked, which left the two a ULP apart; a numeric channel lerping large magnitudes amplifies a ULP of easing by (b - a) (found by the an#86 adversarial review; the loops now match).

It does NOT agree with the CSS solver (css_cubic_bezier()) to 1e-9 for every control point, which is why it stays a named legacy solver until the pixel goldens are re-blessed under the stricter one.

Return type:

float

>>> round(legacy_cubic_bezier(0.0, 0.0, 1.0, 1.0, 0.5), 6)  # linear
0.5
>>> legacy_cubic_bezier(0.42, 0.0, 0.58, 1.0, 0.0), legacy_cubic_bezier(0.42, 0.0, 0.58, 1.0, 1.0)
(0.0, 1.0)