2.5 Animation Code Help (API Needs Extended)?

Hi All,

I am trying to automatically animate the rotation of every object in the selection of a given context.

This code does generate keyframes for the selected object.


def main(context):
    for ob in context.scene.objects:
        if ob.selected == True:
            context.scene.set_frame(1)
            bpy.ops.anim.keyframe_insert_menu(type=-2, confirm_success=False, always_prompt=False)
            context.scene.set_frame(10)
            bpy.ops.transform.rotate(value=(0.930478,), axis=(-0, -0, -1), proportional='DISABLED', proportional_editing_falloff='SMOOTH', proportional_size=1, mirror=False, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0))
            bpy.ops.anim.keyframe_insert_menu(type=-2, confirm_success=False, always_prompt=False)

But when I fetch the command that is spit out by the console in report mode for the extrapolation, it does not work.


bpy.ops.graph.extrapolation_type(type='LINEAR')

My guess is I am no longer referencing the correct context. So how do I dig down into the context I have to set the extrapolation type?

Thanks!

This operator only works from the Graph Editor. Using it elsewhere will fail.

Yeah, I made some progress!


ob.animation_data.action.fcurves[1].extrapolation = 'LINEAR'

I put the outliner in datablocks mode and started digging down until I found a possible bpy path to the data. The obvious problem is my hard coded 1. So I need a way to interrogate the fcurves to look specifically for the curve I just created.

A simpler solution would be if keyframe_insert actually returned the fcurve it just operated upon, but it does not look like it does. This would be quite convenient if it did.


def main(context):
    for ob in context.scene.objects:
        if ob.selected == True:
            context.scene.set_frame(1)
            bpy.ops.anim.keyframe_insert_menu(type=-2, confirm_success=False, always_prompt=False)
            context.scene.set_frame(10)
            bpy.ops.transform.rotate(value=(0.930478,), axis=(-0, -0, -1), proportional='DISABLED', proportional_editing_falloff='SMOOTH', proportional_size=1, mirror=False, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0))
            k2 = bpy.ops.anim.keyframe_insert(type=-2, confirm_success=False)
            ob.animation_data.action.fcurves[1].extrapolation = 'LINEAR'
            print(ob.animation_data.action.fcurves[0].extrapolation)
            #print(dir(ob.animation_data.action.fcurves[0].extrapolation(type=LINEAR)))
            #bpy.ops.graph.extrapolation_type(type='LINEAR')

Once again print(dir(myVar)) is invaluable in debugging. I hope to refine this still.

This is really close, but it only works if I run it 2 times.


def main(context):
    for ob in context.scene.objects:
        if ob.selected == True:
    
            context.scene.set_frame(1)
            bpy.ops.transform.rotate(value=(0.0,), axis=(-0, -0, -1), proportional='DISABLED', proportional_editing_falloff='SMOOTH', proportional_size=1, mirror=False, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0))
            bpy.ops.transform.rotate(value=(0.0,), axis=(-0, -1, -0), proportional='DISABLED', proportional_editing_falloff='SMOOTH', proportional_size=1, mirror=False, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0))
            bpy.ops.transform.rotate(value=(0.0,), axis=(-1, -0, -0), proportional='DISABLED', proportional_editing_falloff='SMOOTH', proportional_size=1, mirror=False, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0))
            bpy.ops.anim.keyframe_insert_menu(type=-2, confirm_success=False, always_prompt=False)
            rndFrame = 30 + (random() * 120)
            rndXRot = random()
            rndYRot = random()
            rndZRot = random()
            print ("RND Values = " + str(rndFrame) + ", [" + str(rndXRot) + ", " + str(rndYRot) + ", " + str(rndZRot) + "]")
            context.scene.set_frame(int(rndFrame))
            bpy.ops.transform.rotate(value=(rndZRot,), axis=(-0, -0, -1), proportional='DISABLED', proportional_editing_falloff='SMOOTH', proportional_size=1, mirror=False, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0))
            bpy.ops.transform.rotate(value=(rndYRot,), axis=(-0, -1, -0), proportional='DISABLED', proportional_editing_falloff='SMOOTH', proportional_size=1, mirror=False, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0))
            bpy.ops.transform.rotate(value=(rndXRot,), axis=(-1, -0, -0), proportional='DISABLED', proportional_editing_falloff='SMOOTH', proportional_size=1, mirror=False, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0))
            k2 = bpy.ops.anim.keyframe_insert(type=-2, confirm_success=False)
            ob.animation_data.action.fcurves[0].extrapolation = 'LINEAR'
            ob.animation_data.action.fcurves[1].extrapolation = 'LINEAR'
            ob.animation_data.action.fcurves[2].extrapolation = 'LINEAR'

I assume I need to to some kind of refresh or update on the fcurve. Does anyone know how to do this?

I have gotten a little further, but the operator still only works correctly if you run it twice.
The extrapolation for the fcurve does not update until you run it again even though I can see in the data blocks browser that the code is indeed setting the Extrapolation to Linear.
My guess is the fCurve itself needs an update() fnuction which does not yet exist.
Also I am updating the scene as well and that does not help.

Here is the code for the entire operator.
USAGE: Copy this code into a text window, select the default cube or any object and press ALT-P in the code window.


# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ##### END GPL LICENSE BLOCK #####

import bpy      # I'm not sure if this is really needed?
from random import random

"""
Name: 'Randomly Animate Rotation'
Blender: 250
"""
__author__ = ["Atom"]
__version__ = '0.1'
__url__ = ["http://blenderartists.org/forum/showthread.php?t=177757"]
__bpydoc__ = """
Usage:

Select a group of objects and activate the operator.

This script will randomly animate the XYZ of every object in the selection so they continuously animate.

Version history
v0.1 - Initial draft as an operator, no user interface.
"""
def main(context):
    for ob in context.scene.objects:
        if ob.selected == True:
    
            context.scene.set_frame(1)
            bpy.ops.transform.rotate(value=(0.0,), axis=(-0, -0, -1), proportional='DISABLED', proportional_editing_falloff='SMOOTH', proportional_size=1, mirror=False, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0))
            bpy.ops.transform.rotate(value=(0.0,), axis=(-0, -1, -0), proportional='DISABLED', proportional_editing_falloff='SMOOTH', proportional_size=1, mirror=False, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0))
            bpy.ops.transform.rotate(value=(0.0,), axis=(-1, -0, -0), proportional='DISABLED', proportional_editing_falloff='SMOOTH', proportional_size=1, mirror=False, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0))
            bpy.ops.anim.keyframe_insert_menu(type=-2, confirm_success=False, always_prompt=False)
            rndFrame = 60
            rndXRot = random()
            rndYRot = random()
            rndZRot = random()
            #print ("RND Values = " + str(rndFrame) + ", [" + str(rndXRot) + ", " + str(rndYRot) + ", " + str(rndZRot) + "]")
            context.scene.set_frame(int(rndFrame))
            bpy.ops.transform.rotate(value=(rndZRot,), axis=(-0, -0, -1), proportional='DISABLED', proportional_editing_falloff='SMOOTH', proportional_size=1, mirror=False, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0))
            bpy.ops.transform.rotate(value=(rndYRot,), axis=(-0, -1, -0), proportional='DISABLED', proportional_editing_falloff='SMOOTH', proportional_size=1, mirror=False, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0))
            bpy.ops.transform.rotate(value=(rndXRot,), axis=(-1, -0, -0), proportional='DISABLED', proportional_editing_falloff='SMOOTH', proportional_size=1, mirror=False, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0))
            bpy.ops.anim.keyframe_insert_menu(type=-2, confirm_success=False, always_prompt=False)
            for fcurve in ob.animation_data.action.fcurves:
                if fcurve.data_path == "rotation_euler":
                    fcurve.extrapolation = "LINEAR"

            #var = bpy.ops.anim
            #print(var)
            #print(dir(var))

    context.scene.update()


class RandomlyAnimateRotation(bpy.types.Operator):
    # Tool tip for operator goes between these tripple single quotes.
    '''Randomly animate the XYZ rotation of all objects in the selection.'''
    bl_idname = "object.random_animate_rotation"
    bl_label = "Randomly animate the XYZ rotation of all objects in the selection."

    def poll(self, context):
        return context.active_object != None

    def execute(self, context):
        print ("--> BEGIN: Operator - RandomlyAnimateRotation")
        main(context)
        print ("<--END: Operator - RandomlyAnimateRotation
")
        return {'FINISHED'}

bpy.types.register(RandomlyAnimateRotation)

if __name__ == "__main__":
    bpy.ops.object.random_animate_rotation()    # Must match bl_idname?

setting the extrapolation of the fcurve,
could you try to set the extrapolation directly after the creation of the fcurve?
Not at the end, after more than 1 point is created.
It works for me if the extrapolation is changed after the first point is created.
Thats directly after the first keyframe_insert_menu.
Changing the extrapolation later (if there are more than 1 point) seems only
to work out of the graph(fcurve)-editor - the data-entry of the fcurve gets changed,
but there is no recalculation for the whole fcurve.
Btw. because a recalculation would need some time for many points, it would
be the best to set the extrapolation short after the creation of the fcurve.

? there maybe a recalculation-function in the context of the graph(fcurve)editor-window …
but i dont know…

another try … workedt — same ?
setting extrapolation two times, first after first keyframe_insert and a second time
at the end of the function(after second keyframe_insert).
(test is a part of my name … ;-] )