3/21/2017 - 12:58 AM

anim echo ip

anim echo ip

import pymel.core as pm
import anim_values as 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,
                                     value_fade_up=value_fade_up, time_amplitude=time_amplitude,
                                     value_amplitude=value_amplitude, time_curve_type=time_curve_type,
        self.v_scale_values = self.create_scale_values(
            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.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)
            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']
            v_scales = self.v_scale_values['down_values']

        if self.scale_parameters['time_fade_up']:
            t_scales = self.t_scale_values['up_values']
            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,