thi-ng/umbrella

View on GitHub
packages/shader-ast-stdlib/src/math/easing.ts

Summary

Maintainability
C
1 day
Test Coverage
import type { FnBody1 } from "@thi.ng/shader-ast";
import { F } from "@thi.ng/shader-ast/api/types";
import { ifThen, ternary } from "@thi.ng/shader-ast/ast/controlflow";
import { defn, ret } from "@thi.ng/shader-ast/ast/function";
import {
    FLOAT0,
    FLOAT05,
    FLOAT1,
    FLOAT2,
    HALF_PI,
    PI,
    TAU,
    float,
} from "@thi.ng/shader-ast/ast/lit";
import {
    add,
    div,
    eq,
    gte,
    lt,
    lte,
    madd,
    mul,
    neg,
    reciprocal,
    sub,
} from "@thi.ng/shader-ast/ast/ops";
import { cos, exp2, pow, sin, sqrt } from "@thi.ng/shader-ast/builtin/math";

/**
 * Higher order helper function to wrap a given easing function body as proper
 * shader-ast function.
 *
 * @param body
 */
export const defEasing = (body: FnBody1<"float">) => defn(F, null, [F], body);

export const easeInSine = defEasing((x) => [
    ret(sub(FLOAT1, cos(mul(x, HALF_PI)))),
]);

export const easeOutSine = defEasing((x) => [ret(sin(mul(x, HALF_PI)))]);

export const easeInOutSine = defEasing((x) => [
    ret(div(neg(sub(cos(mul(PI, x)), FLOAT1)), FLOAT2)),
]);

export const easeInQuad = defEasing((x) => [ret(pow(x, FLOAT2))]);

export const easeOutQuad = defEasing((x) => [
    ret(sub(FLOAT1, pow(sub(FLOAT1, x), FLOAT2))),
]);

export const easeInOutQuad = defEasing((x) => [
    ret(
        ternary(
            lt(x, FLOAT05),
            mul(FLOAT2, pow(x, FLOAT2)),
            sub(FLOAT1, div(pow(madd(neg(FLOAT2), x, FLOAT2), FLOAT2), FLOAT2))
        )
    ),
]);

export const easeInCubic = defEasing((x) => [ret(pow(x, float(3)))]);

export const easeOutCubic = defEasing((x) => [
    ret(sub(FLOAT1, pow(sub(FLOAT1, x), float(3)))),
]);

export const easeInOutCubic = defEasing((x) => [
    ret(
        ternary(
            lt(x, FLOAT05),
            mul(float(4), pow(x, float(3))),
            sub(
                FLOAT1,
                div(pow(madd(neg(FLOAT2), x, FLOAT2), float(3)), FLOAT2)
            )
        )
    ),
]);

export const easeInQuart = defEasing((x) => [ret(pow(x, float(4)))]);

export const easeOutQuart = defEasing((x) => [
    ret(sub(FLOAT1, pow(sub(FLOAT1, x), float(4)))),
]);

export const easeInOutQuart = defEasing((x) => [
    ret(
        ternary(
            lt(x, FLOAT05),
            mul(float(8), pow(x, float(4))),
            sub(
                FLOAT1,
                div(pow(madd(neg(FLOAT2), x, FLOAT2), float(4)), FLOAT2)
            )
        )
    ),
]);

export const easeInQuint = defEasing((x) => [ret(pow(x, float(5)))]);

export const easeOutQuint = defEasing((x) => [
    ret(sub(FLOAT1, pow(sub(FLOAT1, x), float(5)))),
]);

export const easeInOutQuint = defEasing((x) => [
    ret(
        ternary(
            lt(x, FLOAT05),
            mul(float(16), pow(x, float(5))),
            sub(
                FLOAT1,
                div(pow(madd(neg(FLOAT2), x, FLOAT2), float(5)), FLOAT2)
            )
        )
    ),
]);

export const easeInExpo = defEasing((x) => [
    ret(
        ternary(eq(x, FLOAT0), FLOAT0, exp2(sub(mul(float(10), x), float(10))))
    ),
]);

export const easeOutExpo = defEasing((x) => [
    ret(
        ternary(
            eq(x, FLOAT1),
            FLOAT1,
            sub(FLOAT1, exp2(mul(neg(float(10)), x)))
        )
    ),
]);

export const easeInOutExpo = defEasing((x) => [
    ret(
        ternary(
            eq(x, FLOAT0),
            FLOAT0,
            ternary(
                eq(x, FLOAT1),
                FLOAT1,
                ternary(
                    lt(x, FLOAT05),
                    div(exp2(sub(mul(float(20), x), float(10))), FLOAT2),
                    div(
                        sub(FLOAT2, exp2(madd(neg(float(20)), x, float(10)))),
                        FLOAT2
                    )
                )
            )
        )
    ),
]);

export const easeInCirc = defEasing((x) => [
    ret(sub(FLOAT1, sqrt(sub(FLOAT1, pow(x, FLOAT2))))),
]);

export const easeOutCirc = defEasing((x) => [
    ret(sqrt(sub(FLOAT1, pow(sub(x, FLOAT1), FLOAT2)))),
]);

export const easeInOutCirc = defEasing((x) => [
    ret(
        ternary(
            lt(x, FLOAT05),
            div(
                sub(FLOAT1, sqrt(sub(FLOAT1, pow(mul(FLOAT2, x), FLOAT2)))),
                FLOAT2
            ),
            div(
                add(
                    sqrt(
                        sub(FLOAT1, pow(madd(neg(FLOAT2), x, FLOAT2), FLOAT2))
                    ),
                    FLOAT1
                ),
                FLOAT2
            )
        )
    ),
]);

export const easeInBack = defEasing((x) => {
    const c1 = 1.70158;
    const c3 = c1 + 1;

    return [ret(sub(mul(c3, pow(x, float(3))), mul(c1, pow(x, FLOAT2))))];
});

export const easeOutBack = defEasing((x) => {
    const c1 = 1.70158;
    const c3 = c1 + 1;

    return [
        ret(
            add(
                madd(float(c3), pow(sub(x, FLOAT1), float(3)), FLOAT1),
                mul(c1, pow(sub(x, FLOAT1), FLOAT2))
            )
        ),
    ];
});

export const easeInOutBack = defEasing((x) => {
    const c1 = 1.70158;
    const c2 = c1 * 1.525;

    return [
        ret(
            ternary(
                lt(x, FLOAT05),
                div(
                    mul(
                        pow(mul(FLOAT2, x), FLOAT2),
                        sub(mul(mul(add(c2, FLOAT1), FLOAT2), x), c2)
                    ),
                    FLOAT2
                ),
                div(
                    add(
                        mul(
                            pow(sub(mul(FLOAT2, x), FLOAT2), FLOAT2),
                            add(
                                mul(
                                    add(c2, FLOAT1),
                                    sub(mul(x, FLOAT2), FLOAT2)
                                ),
                                c2
                            )
                        ),
                        FLOAT2
                    ),
                    FLOAT2
                )
            )
        ),
    ];
});

export const easeInElastic = defEasing((x) => [
    ret(
        ternary(
            eq(x, FLOAT0),
            FLOAT0,
            ternary(
                eq(x, FLOAT1),
                FLOAT1,
                mul(
                    neg(exp2(sub(mul(10, x), 10))),
                    sin(mul(sub(mul(x, 10), 10.75), div(TAU, 3)))
                )
            )
        )
    ),
]);

export const easeOutElastic = defEasing((x) => [
    ret(
        ternary(
            lte(x, FLOAT0),
            FLOAT0,
            ternary(
                gte(x, FLOAT1),
                FLOAT1,
                madd(
                    exp2(mul(-10, x)),
                    sin(mul(sub(mul(x, 10), 0.75), div(TAU, 3))),
                    FLOAT1
                )
            )
        )
    ),
]);

export const easeInOutElastic = defEasing((x) => {
    const c5 = div(TAU, 4.5);

    return [
        ret(
            ternary(
                eq(x, FLOAT0),
                FLOAT0,
                ternary(
                    eq(x, FLOAT1),
                    FLOAT1,
                    ternary(
                        lt(x, FLOAT05),
                        div(
                            neg(
                                mul(
                                    exp2(sub(mul(20, x), 10)),
                                    sin(mul(sub(mul(20, x), 11.125), c5))
                                )
                            ),
                            FLOAT2
                        ),
                        add(
                            div(
                                mul(
                                    exp2(madd(neg(float(20)), x, float(10))),
                                    sin(mul(sub(mul(20, x), 11.125), c5))
                                ),
                                FLOAT2
                            ),
                            FLOAT1
                        )
                    )
                )
            )
        ),
    ];
});

export const easeOutBounce = defEasing((x) => {
    const n1 = 7.5625;
    const d1 = float(2.75);

    return [
        ifThen(lt(x, reciprocal(d1)), [ret(mul(n1, pow(x, FLOAT2)))]),
        ifThen(lt(x, div(FLOAT2, d1)), [
            ret(
                add(
                    mul(mul(n1, sub(x, div(1.5, d1))), sub(x, div(1.5, d1))),
                    0.75
                )
            ),
        ]),
        ifThen(lt(x, div(2.5, d1)), [
            ret(
                add(
                    mul(mul(n1, sub(x, div(2.25, d1))), sub(x, div(2.25, d1))),
                    0.9375
                )
            ),
        ]),
        ret(
            add(
                mul(mul(n1, sub(x, div(2.625, d1))), sub(x, div(2.625, d1))),
                0.984375
            )
        ),
    ];
});

export const easeInBounce = defEasing((x) => [
    ret(sub(FLOAT1, easeOutBounce(sub(FLOAT1, x)))),
]);

export const easeInOutBounce = defEasing((x) => [
    ret(
        ternary(
            lt(x, FLOAT05),
            div(
                sub(FLOAT1, easeOutBounce(sub(FLOAT1, mul(FLOAT2, x)))),
                FLOAT2
            ),
            div(add(FLOAT1, easeOutBounce(sub(mul(FLOAT2, x), FLOAT1))), FLOAT2)
        )
    ),
]);