Skip to content

Instantly share code, notes, and snippets.

@UnaiM
Forked from lcrs/retimestab.py
Last active September 24, 2018 19:33
Show Gist options
  • Save UnaiM/0cdc1eb1ae8b86a90908db970156579a to your computer and use it in GitHub Desktop.
Save UnaiM/0cdc1eb1ae8b86a90908db970156579a to your computer and use it in GitHub Desktop.
# 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