anim echo ip
import pymel.core as pm
import anim_values as av
reload(av)
class AnimEcho:
"""
Copies selections of keyframes the given number of times, scaling them up or down gradually
Pivots are: 'highest', 'lowest', 'mid', anything else is 0
"""
def __init__(self, iterations, pivot, curves, do_time_scale=True, do_value_scale=True,
time_fade_up=True, value_fade_up=True, time_amplitude=1.0, value_amplitude=1.0,
time_curve_type='hard_out_hard_in', value_curve_type='hard_out_hard_in'):
self.iterations = iterations
self.scale_parameters = dict(do_time_scale=do_time_scale, time_fade_up=time_fade_up,
do_value_scale=do_value_scale,
value_fade_up=value_fade_up, time_amplitude=time_amplitude,
value_amplitude=value_amplitude, time_curve_type=time_curve_type,
value_curve_type=value_curve_type)
self.v_scale_values = self.create_scale_values(
self.scale_parameters['value_amplitude'],
self.scale_parameters['value_curve_type']) # 2 lists, scale up values, scale down values
self.t_scale_values = self.create_scale_values(self.scale_parameters['time_amplitude'],
self.scale_parameters['time_curve_type'])
self.curves = curves
self.pivot = pivot
def get_frame(self, curve, which_frame):
"""Returns the frame of the start or ending key of a curve"""
key_times = pm.keyframe(curve, query=True, timeChange=True)
keys = {'start': key_times[0],
'end': key_times[-1]}
return keys[which_frame]
def create_scale_values(self, amplitude, curve_type):
"""Returns 2 lists, the first is scale up values, send is scale down"""
# build the list using amplitude, add an extra iteration to avoid the last value being 0.0
initial_values = av.generate_multipliers(self.iterations, curve_type)
scales_up = [1.0 + ((amplitude - 1.0) * i) for i in initial_values]
# don't need amplitude because we're scaling down
scales_down = [1.0 - i for i in initial_values]
for i, s in enumerate(scales_down):
if s < 0.01:
scales_down[i] = 0.01
print('scales up: {}, scales down: {}'.format(scales_up, scales_down))
return dict(up_values=scales_up, down_values=scales_down)
# def create_scale_values(self, amplitude):
# """Returns 2 lists, the first is scale up values, send is scale down"""
# # build the list using amplitude, add an extra iteration to avoid the last value being 0.0
# scales_up = [1.0 + ((amplitude - 1.0) / self.iterations) * i for i in range(self.iterations + 1)]
#
# # don't need amplitude because we're scaling down
# scales_down = [1.0 - (1.0 / (self.iterations + 1)) * i for i in range(self.iterations + 1)]
#
# del scales_up[0], scales_down[0] # avoid the first copy not being scaled because it is 1.0
#
# return dict(up_values=scales_up, down_values=scales_down)
def get_pivot(self, curve):
"""Returns the specified pivot value on the curve"""
key_values = pm.keyframe(curve, query=True, valueChange=True)
if self.pivot == 'mid':
return (max(key_values) + min(key_values)) / 2
elif self.pivot == 'highest':
return max(key_values)
elif self.pivot == 'lowest':
return min(key_values)
else:
return 0
def echo_curves(self):
"""Does the fade operation on selected keys/curves"""
if self.scale_parameters['value_fade_up']:
v_scales = self.v_scale_values['up_values']
else:
v_scales = self.v_scale_values['down_values']
if self.scale_parameters['time_fade_up']:
t_scales = self.t_scale_values['up_values']
else:
t_scales = self.t_scale_values['down_values']
for curve in self.curves:
# establish the initial starting values
start = self.get_frame(curve, 'start')
end = self.get_frame(curve, 'end')
value_pivot = self.get_pivot(curve)
print('initial start, end: {} {}'.format(start, end))
# copy the keys and store the frame length of them
pm.copyKey(curve, time=(start, end))
sel_length = abs(end - start)
# iterate through each of the copies of the selection, determined by iterations
for i in range(self.iterations):
start = end # next paste starting point is the current end of the selection
end = end + sel_length # new end point is current start + original length since it hasn't scaled yet
pm.pasteKey(curve, edit=True, time=(start,))
print('start, end before next loop: {} {}'.format(start, end))
if self.scale_parameters['do_value_scale']:
pm.scaleKey(curve, valueScale=v_scales[i], valuePivot=value_pivot, time=(start, end))
if self.scale_parameters['do_time_scale']:
pm.scaleKey(curve, timeScale=t_scales[i], timePivot=start, time=(start, end))
# pm.snapKey(curve) # snap the keys to avoid subframes
end = self.get_frame(curve, 'end') # get the new end of the curve since there's been scaling done
# it's possible the amplitude can scale the curves beyond the original pivot values, so update them
# to avoid weird results
if self.pivot == 'highest' or self.pivot == 'lowest':
value_pivot = self.get_pivot(curve)
print('end after loop: {}'.format(end))
selected_curves = pm.keyframe(q=True, selected=True, name=True)
if len(selected_curves) > 0:
fader = AnimEcho(iterations=8, pivot='', curves=selected_curves, do_time_scale=True, do_value_scale=True,
time_fade_up=False, value_fade_up=False, time_amplitude=3, value_amplitude=3,
value_curve_type='hard_out_ease_in')
fader.echo_curves()