# How can I repeat a group of animation phases

I am building an animation of a machine where various parts move along paths in turn. So far I have done this without any armatures. I was hoping to string several cycles of operation together, and modify object parameters (using python script) for each machine cycle.

From previous questions, it seems that the NLA editor can't work with groups of actions associated with multiple objects - so maybe the best I can achieve is to write a script to step and repeat the keyframes for all objects? Alternatively, is it possible to render-time transpose my keyframes on the fly at the end of the cycle, or programatically render frames 1..300 twice (with some tweaks) to achieve 1..600?

There are two reasons for wishing to automate the process. Firstly, it would allow me to generate any number of different cycles based on a small number of parameters (my first trial has 5 cycles of 300 frames). Second, as I have already discovered, defects in my base model only become obvious after I made and modified 5 copies of the basic cycle.

(for what its worth, I'm not an artist - just trying to find a good way to demonstrate how the machine works)

• A frame change handler might be a solution: blender.org/api/blender_python_api_2_75_release/… Sep 1 '15 at 23:46
• I have used NLAs on multiple objects that share actions. youtube.com/watch?v=2QCKXoAgHl4 There are only 3 actions on the balls of that animation. the high-frequency wave, and up/down low frequency waves. They're combined in the NLA with different time offsets for each ball. Sep 2 '15 at 21:06

If you need help figuring out how to rig NLAs with python, perhaps this sample code from my virtual kinetic art helps:

import bpy
from math import *

def set_kp(kp, co, easing):
kp.co = co
kp.handle_left = co
kp.handle_right = co
kp.easing = easing
kp.interpolation = 'SINE'

def wipe_fcurves(action):
while len(action.fcurves) > 0:
action.fcurves.remove(action.fcurves[-1])

def keyframe_action1(action):
wipe_fcurves(action)

fc = action.fcurves.new("location", 2)
set_kp(fc.keyframe_points[0], [0,0], 'EASE_OUT')
set_kp(fc.keyframe_points[1], [20,1], 'EASE_IN')
set_kp(fc.keyframe_points[2], [40,0], 'EASE_OUT')
set_kp(fc.keyframe_points[3], [60,-1], 'EASE_IN')
set_kp(fc.keyframe_points[4], [80,0], 'EASE_OUT')

def keyframe_action2(action, scale=1):

wipe_fcurves(action)
timescale = 400
fc = action.fcurves.new("location", 2)
set_kp(fc.keyframe_points[0], [0,0], 'EASE_IN')
set_kp(fc.keyframe_points[1], [1*timescale,1*scale], 'EASE_OUT')
set_kp(fc.keyframe_points[2], [2*timescale,2*scale], 'EASE_IN')
set_kp(fc.keyframe_points[3], [3*timescale,1*scale], 'EASE_OUT')
set_kp(fc.keyframe_points[4], [4*timescale,0], 'EASE_IN')

def action_for(u,v):

name = "high frequency sine"
action = bpy.data.actions.get(name)
if action is None:
action = bpy.data.actions.new(name)
keyframe_action1(action)

return action

def action2a():
name = "low frequency cosine up"
action = bpy.data.actions.get(name)
if action is None:
action = bpy.data.actions.new(name)
keyframe_action2(action, 2)
return action

def action2b():
name = "low frequency cosine down"
action = bpy.data.actions.get(name)
if action is None:
action = bpy.data.actions.new(name)
keyframe_action2(action, -2)
return action

def rig_track_one_strip(ad, track_no, strip_start_frame, action, source_start, source_end, repeat_count, strip_scale=1,
track_name="high frequency", blend_type='REPLACE'):
else:
tr1.name = track_name
if len(tr1.strips) < 1:
strip1 = tr1.strips.new("bacon", strip_start_frame, action)
else:
strip1 = tr1.strips[0]
strip1.action = action
strip1.frame_start = strip_start_frame
strip1.action_frame_start = source_start
strip1.action_frame_end = source_end
strip1.repeat = repeat_count
strip1.scale = strip_scale
strip1.blend_type = blend_type

return (tr1, strip1)

def rig_nlas(obj, u,v):
theta = u/3+v/10 # + frame*pi/40

if obj.animation_data is None:
obj.animation_data_create()

if 0 == u % 2:
repeat = 40
scale = 1
else:
repeat = 38
scale = 40 / 38
rig_track_one_strip(ad, 0, -theta / (pi / 40), action_for(u, v), 0,80, repeat, scale)

if 0==u%2:
else:

def fab_object_for(scn, u,v):
name = "ball2 at %d,%d" % (u, v)
obj = bpy.data.objects.get(name)
if obj is None:
obj = bpy.data.objects.new(name, bpy.data.meshes.get("Icosphere"))
try:
except:
pass
obj.location = (u,v,0)
rig_nlas(obj, u, v)

scn = bpy.context.scene

for v in range(20):
for u in range(20):
fab_object_for(scn ,u,v)

keyframe_action1(bpy.data.actions['high frequency sine'])
keyframe_action2(bpy.data.actions['low frequency cosine down'], -2)
keyframe_action2(bpy.data.actions['low frequency cosine up'], 2)


It assumes you already have a mesh named 'Icosphere' (you can add one using blender's interactive menus).

Does it need to be programmatic? If not, select one of the objects, select the keyframes (in the graph editor) in frames 1-300:

1. Hit Shift+D to duplicate the keyframes
2. Then hit G and 300
3. Then Enter to confirm the operation

This will duplicate the exact keyframe in 1-300 and move the duplicates to 301-600.

If it is more complicated than that, perhaps you might update your question with more details on exactly what you are attempting to accomplish.