Skip to content

Instantly share code, notes, and snippets.

@sarimarton
Last active July 24, 2022 22:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sarimarton/46decb1cd355df46fc5463468ced2bad to your computer and use it in GitHub Desktop.
Save sarimarton/46decb1cd355df46fc5463468ced2bad to your computer and use it in GitHub Desktop.
Avisynth+ bezier-easing function
# 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