Last active
July 24, 2022 22:18
-
-
Save sarimarton/46decb1cd355df46fc5463468ced2bad to your computer and use it in GitHub Desktop.
Avisynth+ bezier-easing function
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ported from https://github.com/gre/bezier-easing/blob/master/src/index.js | |
# Takes CSS-compliant bezier args and an X value and returns the Y value. | |
# Playground for bezier args: https://cubic-bezier.com/ | |
# Example usage: | |
# scripted = ScriptClip(part, """ | |
# x = float(current_frame) / float(len) | |
# val = bezier(1, 0.55, 0.45, 1, 0.5) | |
# overlay(blankclip(c, color=color), opacity=(val)) | |
# """, args="len,color,c", local=true) | |
# /** | |
# * https://github.com/gre/bezier-easing | |
# * BezierEasing - use bezier curve for transition easing function | |
# * by Gaëtan Renaudeau 2014 - 2015 – MIT License | |
# */ | |
# These values are established by empiricism with tests (tradeoff: performance VS precision) | |
global NEWTON_ITERATIONS = 4 | |
global NEWTON_MIN_SLOPE = 0.001 | |
global SUBDIVISION_PRECISION = 0.0000001 | |
global SUBDIVISION_MAX_ITERATIONS = 10 | |
global kSplineTableSize = 11 | |
global kSampleStepSize = 1.0 / (kSplineTableSize - 1.0) | |
function A(float aA1, float aA2) { | |
return 1.0 - 3.0 * aA2 + 3.0 * aA1 | |
} | |
function B(float aA1, float aA2) { | |
return 3.0 * aA2 - 6.0 * aA1 | |
} | |
function C(float aA1) { | |
return 3.0 * aA1 | |
} | |
function calcBezier(float aT, float aA1, float aA2) { | |
return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT | |
} | |
function getSlope(float aT, float aA1, float aA2) { | |
return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1) | |
} | |
function binarySubdivide(float aX, float aA, float aB, float mX1, float mX2) { | |
currentX = 0.0 | |
currentT = 0.0 | |
i = 0 | |
cond = true | |
while (cond) { | |
currentT = aA + (aB - aA) / 2.0 | |
currentX = calcBezier(currentT, mX1, mX2) - aX | |
if (currentX > 0.0) { | |
aB = currentT | |
} else { | |
aA = currentT | |
} | |
i = i + 1 | |
cond = abs(currentX) > SUBDIVISION_PRECISION && i < SUBDIVISION_MAX_ITERATIONS | |
} | |
return currentT | |
} | |
function newtonRaphsonIterate(float aX, float aGuessT, float mX1, float mX2) { | |
i = 0 | |
while (i < NEWTON_ITERATIONS) { | |
currentSlope = getSlope(aGuessT, mX1, mX2) | |
if (currentSlope == 0.0) { | |
return aGuessT | |
} | |
currentX = calcBezier(aGuessT, mX1, mX2) - aX | |
aGuessT = aGuessT - currentX / currentSlope | |
i = i + 1 | |
} | |
return aGuessT | |
} | |
function getTForX(float aX, float mX1, float mX2, float sv0, float sv1, float sv2, float sv3, float sv4, float sv5, float sv6, float sv7, float sv8, float sv9, float sv10) { | |
intervalStart = 0.0 | |
currentSample = 1 | |
lastSample = kSplineTableSize - 1 | |
while (currentSample != lastSample && eval("sv" + String(currentSample)) <= aX) { | |
intervalStart = intervalStart + kSampleStepSize | |
currentSample = currentSample + 1 | |
} | |
currentSample = currentSample - 1 | |
dist = (aX - eval("sv" + String(currentSample))) / (eval("sv" + String(currentSample + 1)) - eval("sv" + String(currentSample))) | |
guessForT = intervalStart + dist * kSampleStepSize | |
initialSlope = getSlope(guessForT, mX1, mX2) | |
if (initialSlope >= NEWTON_MIN_SLOPE) { | |
return newtonRaphsonIterate(aX, guessForT, mX1, mX2) | |
} else if (initialSlope == 0.0) { | |
return guessForT | |
} else { | |
return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2) | |
} | |
} | |
function bezier(float mX1, float mY1, float mX2, float mY2, float x) { | |
if (mX1 == mY1 && mX2 == mY2) { | |
return x | |
} | |
# sample values | |
global sv0 = calcBezier(0 * kSampleStepSize, mX1, mX2) | |
global sv1 = calcBezier(1 * kSampleStepSize, mX1, mX2) | |
global sv2 = calcBezier(2 * kSampleStepSize, mX1, mX2) | |
global sv3 = calcBezier(3 * kSampleStepSize, mX1, mX2) | |
global sv4 = calcBezier(4 * kSampleStepSize, mX1, mX2) | |
global sv5 = calcBezier(5 * kSampleStepSize, mX1, mX2) | |
global sv6 = calcBezier(6 * kSampleStepSize, mX1, mX2) | |
global sv7 = calcBezier(7 * kSampleStepSize, mX1, mX2) | |
global sv8 = calcBezier(8 * kSampleStepSize, mX1, mX2) | |
global sv9 = calcBezier(9 * kSampleStepSize, mX1, mX2) | |
global sv10 = calcBezier(10 * kSampleStepSize, mX1, mX2) | |
if (x == 0 || x == 1) { | |
return x | |
} | |
return calcBezier(getTForX(x, mX1, mX2, sv0, sv1, sv2, sv3, sv4, sv5, sv6, sv7, sv8, sv9, sv10), mY1, mY2) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment