-
-
Save UnaiM/0cdc1eb1ae8b86a90908db970156579a to your computer and use it in GitHub Desktop.
This file contains 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
# Retime a tracked object to better fit a target curve | |
# Select two Transform nodes: the first with a jerky tracked curve, the | |
# second with a smooth target curve | |
# Group effort by Lewis Saunders, Unai Martínez Barredo, Erwan Leroy | |
# Make a function to calculate the distance between two points | |
def distance(a, b): | |
return pow((a[0]-b[0])**2 + (a[1]-b[1])**2, 0.5) | |
# Find the closest point to p on the line ab | |
# Also returns the weight of that point along ab | |
# https://stackoverflow.com/questions/3120357/get-closest-point-to-a-line | |
def interp(a, b, p): | |
if a == b: | |
return None | |
ap = (p[0]-a[0], p[1]-a[1]) | |
ab = (b[0]-a[0], b[1]-a[1]) | |
w = min(1, max(0, (ap[0]*ab[0] + ap[1]*ab[1]) / (ab[0]**2 + ab[1]**2))) | |
return a[0]+ab[0]*w, a[1]+ab[1]*w, w | |
# Here we store the first node in your selection (the latest node you | |
# selected) as 'jerky', and the second one as 'smooth'. | |
smooth, jerky = nuke.selectedNodes()[:2] | |
# Find the first and last frame of your comp and the length in frames | |
root = nuke.Root() | |
first = int(root['first_frame'].value()) | |
last = int(root['last_frame'].value()) | |
clen = last - first + 1 | |
crange = range(clen) | |
# We make two new empty list variables | |
jerkyc = [] | |
smoothc = [] | |
# Now for each frame (that we arbitrarily decided to call i) in the range, we | |
# look at the value of the curves, and add them to the lists defined above | |
for i in crange: | |
j = jerky['translate'].valueAt(first + i) | |
s = smooth['translate'].valueAt(first + i) | |
jerkyc.append(j) | |
smoothc.append(s) | |
# Create an oflow node | |
oflow = nuke.createNode('OFlow2', 'timing2 Frame') | |
# Set the frame knob animated | |
oflow['timingFrame2'].setAnimated() | |
# Go through each frame in the range again... | |
for i in crange: | |
# Take the original list of the jerky values and sort them by | |
# closeness to the value of the smooth curve, then keep the index of | |
# the closest one | |
closest = sorted(crange, key=lambda x: distance(jerkyc[x], smoothc[i]))[0] | |
before = None | |
after = None | |
# If there are points on the jerky curve before or after the closest one, | |
# check if there's a closer match on the lines connecting the closest point | |
# to them | |
if closest > 0: | |
before = interp(jerkyc[closest], jerkyc[closest-1], smoothc[i]) | |
if closest < clen-1: | |
after = interp(jerkyc[closest], jerkyc[closest+1], smoothc[i]) | |
# If there were points in both directions, figure out which one matched | |
# best, and the offset from the closest frame | |
if before != None and after != None: | |
befored = distance(before, smoothc[i]) | |
afterd = distance(after, smoothc[i]) | |
offset = (-before[2], after[2])[(befored, afterd).index(min(befored, afterd))] | |
elif before: | |
offset = -before[2] | |
else: | |
offset = after[2] | |
# Set the value of the timing curve on this frame | |
oflow['timingFrame2'].setValueAt(first+closest+offset, first+i) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment