Need Help Optimizing A Script To Make It Less Time Consuming

I’ve never programmed in python before (my skills lie in C++ and Matlab) which is why I find this type of optimization problem to be extremely difficult. I’m trying to get better and trying to learn how the data in python is handled but this problem is connected to a two month internship with many different parts which is why I don’t really have the time to learn python from scratch. I’m not trying to be lazy though, I am doing python for 12 hour a day, so I’m doing my best! Despite this I require a bit of additional help.

My main problem is that I need to read in a large file with data, add this data to x objects.

My algorithm would look something like this;

Read the file
Add data from file to active object
If data contains a “" or “@”
Duplicate active object
Add data after this "
” or “@” to duplicated object

The code I have so far works, however, when the file contains too much data the time to process this takes an absolute huge amount of time. To process 2003 rows of data takes 1800 seconds on my (fairly good) stationary computer which simply is not acceptable. The worst part is that this is just a fraction of data I would hope to read.

The data looks like this:

x,y,z,x_rot,y_rot,z_rot,frame
0 , 0 , 0 , 0 , 1.106572e-01 , 4.729842e+00 , 1
0 , 0 , 0 , 0 , 1.106572e-01 , 4.747296e+00 , 2
0 , 0 , 0 , 0 , 1.106572e-01 , 4.764749e+00 , 3
0 , 0 , 0 , 0 , 1.106572e-01 , 4.782202e+00 , 4
0 , 0 , 0 , 0 , 1.106572e-01 , 4.799655e+00 , 5
0 , 0 , 0 , 0 , 1.106572e-01 , 4.817109e+00 , 6
0 , 0 , 0 , 0 , 1.106572e-01 , 4.834562e+00 , 7
.
.
.
.
0 , 0 , 0 , 0 , 1.106572e-01 , 4.834562e+00 , 1000
@
 , 0 , 0 , 0 , 1.106572e-01 , 4.729842e+00 , 1
0 , 0 , 0 , 0 , 1.106572e-01 , 4.747296e+00 , 2
0 , 0 , 0 , 0 , 1.106572e-01 , 4.764749e+00 , 3
0 , 0 , 0 , 0 , 1.106572e-01 , 4.782202e+00 , 4
0 , 0 , 0 , 0 , 1.106572e-01 , 4.799655e+00 , 5
0 , 0 , 0 , 0 , 1.106572e-01 , 4.817109e+00 , 6
0 , 0 , 0 , 0 , 1.106572e-01 , 4.834562e+00 , 7
.
.
.
0 , 0 , 0 , 0 , 1.106572e-01 , 4.834562e+00 , 1000


The data is read via the script:

import bpy


anim = []
path = 'C:\\Users\\Inger\\Desktop\\Input\	est.txt' #file pathing
bpy.context.active_object.animation_data_clear() # clear keyframes

file = open(path, 'r')
row = file.read()


for row in row.splitlines():
    elems = row.split(",")


    if elems[0] != "@":
        anim.append((float(elems[0]), float(elems[1]),float(elems[2]),float(elems[3]),                                          
        float(elems[4]),float(elems[5]),int(elems[6])))
        
        ob = bpy.context.object
        for x, y, z, x_rot, y_rot, z_rot, frame in anim:
            ob.location = (x,y,z)
            ob.rotation_euler = (x_rot, y_rot, z_rot)
            ob.keyframe_insert(data_path="location", frame=frame)
            ob.keyframe_insert(data_path="rotation_euler", frame=frame)
    else:
        bpy.ops.object.duplicate()

As the “else” part only occurs once in my test.txt file (only have one “@”) it must mean the reading of the rows and adding data into the list of my first object must be the most time consuming (or computationally heavy) part of this code. I.e This part:

  if elems[0] != "@":
        anim.append((float(elems[0]), float(elems[1]),float(elems[2]),float(elems[3]),                                          
        float(elems[4]),float(elems[5]),int(elems[6])))
        
        ob = bpy.context.object
        for x, y, z, x_rot, y_rot, z_rot, frame in anim:
            ob.location = (x,y,z)
            ob.rotation_euler = (x_rot, y_rot, z_rot)
            ob.keyframe_insert(data_path="location", frame=frame)
            ob.keyframe_insert(data_path="rotation_euler", frame=frame)

My question is, could you help me optimize this snippet of code to take less computational time? Maybe eliminate unnecessary loops or by using different functions entirely?

I will attach an example file of my data that I need to read so you can test this out for yourself if necessary.

Attachments

Script and data.zip (20.7 KB)

Takes <1 sec:

import bpy

anims = []
anim = []

for row in data.splitlines():
    elems = row.split(",")

    if elems[0] != "@":
        anim.append((float(elems[0]), float(elems[1]),float(elems[2]),float(elems[3]),                                          
        float(elems[4]),float(elems[5]),int(elems[6])))
        
    else:
        anims.append(anim)
        anim = []


ob = bpy.context.object

for anim in anims:

    ob.animation_data_clear() # clear keyframes
    ob.animation_data_create()
    ob.animation_data.action = bpy.data.actions.new(name="MyAction")
    
    len_anim = len(anim)
    loc_x = ob.animation_data.action.fcurves.new(data_path="location", index=0)
    loc_x.keyframe_points.add(len_anim)
    loc_y = ob.animation_data.action.fcurves.new(data_path="location", index=1)
    loc_y.keyframe_points.add(len_anim)
    loc_z = ob.animation_data.action.fcurves.new(data_path="location", index=2)
    loc_z.keyframe_points.add(len_anim)
    rot_x = ob.animation_data.action.fcurves.new(data_path="rotation_euler", index=0)
    rot_x.keyframe_points.add(len_anim)
    rot_y = ob.animation_data.action.fcurves.new(data_path="rotation_euler", index=1)
    rot_y.keyframe_points.add(len_anim)
    rot_z = ob.animation_data.action.fcurves.new(data_path="rotation_euler", index=2)
    rot_z.keyframe_points.add(len_anim)

    for i, (x, y, z, x_rot, y_rot, z_rot, frame) in enumerate(anim):
    
        loc_x.keyframe_points[i].co = frame, x
        loc_y.keyframe_points[i].co = frame, y
        loc_z.keyframe_points[i].co = frame, z
        rot_x.keyframe_points[i].co = frame, x_rot
        rot_y.keyframe_points[i].co = frame, y_rot
        rot_z.keyframe_points[i].co = frame, z_rot
        
        #ob.location = (x,y,z)
        #ob.rotation_euler = (x_rot, y_rot, z_rot)
        #ob.keyframe_insert(data_path="location", frame=frame)
        #ob.keyframe_insert(data_path="rotation_euler", frame=frame)
        
        
    #bpy.ops.object.duplicate()
    ob = ob.copy()
    #ob.data = ob.data.copy() # uncomment if you don't want a linked duplicate
    bpy.context.scene.objects.link(ob)

bpy.context.scene.update()

Note: i replaced the file reading for local testing, you need to change that back

That worked very well! I will analyse the code so I understand it properly.

Thank you so much!

I mainly replaced the keyframe_insert by low level functions. It’s more work, but also a lot faster.

I manully create the animation data and add a new action to it to later store animation in it.

For any property that needs to be animated, i create an fcurve and add keyframe points. Then i set the point locations, x is frame number and y is the value

Hey again! I found something that is wrong in the script and I have been trying to solve it all day. I could really use a bump in the right direction.

The scrip provided by CoDEmanX above works perfectly for the rotation.

For example:

0,0,0,0,0,0,1
0,0,0,0,6,0,50

is (almost) a perfect 0 to 2*pi (~6) rotation around the y axis.

The problem is that the script does not copy the original object in a correct manner. If I would just want a duplicate of the original object in x=3 I would write it like this in the data:

0,0,0,0,0,0,1
0,0,0,0,0,0,50
@
3,0,0,0,0,0,1
3,0,0,0,0,0,50

The original arrow is just duplicated and the duplication should have it’s new home in x=3 between frame 1 and 50. This is not what is happening now. In fact nothing is happing if I choose to only use two objects. If I choose more than two objects, the object behaves in a strange manner, almost like it starts from x=0 and goes to x=3 instead of just sticking to x = 3 throughout the animation.

Could anyone please point me in the direction to what might be wrong? The script can be found in CoDEmanX answer above. Any help is appreciated!

can you provide a test file (data)?

Certainly! See the attached file. I made an example file that is much smaller that the actual data, but it covers every type of case that would be handled for the real data. This way it’s much simpler to see if the script acts in the way we want.

The file contains data for 6 objects acting in frames 1 to 10.
The first three objects just creates a duplicate of the original; sets one of them in x=5 and the other one in x=10. They are stationary between frame 1-10 and are there to show that the script can duplicate properly.

The two following objects are created in y=5 and y=10. The first object will make a 360 degree revolution around it’s pivot point in the x-plane. The second object will do the same revolution except in the y=10 position around the z-plane.

The last object will move and make a 360 degree revolution at the same time. It will start off at y=5 (frame 1) move itself to y=1 (frame 5) and then move back to y=5 again (frame 10).

You will find the file in the attached zip folder.

Attachments

test.zip (333 Bytes)

I solved this issue myself. Thanks for all the help though!