Tuesday, February 16, 2016

Blender Script for object-motion measurements

In Blender, given an object that has a motion path, will provide animated empties which indicate the object's velocity, acceleration, magnitude, and accumulated distance moved.


# ---------
# Tested on Blender 2.75a and 2.76
# For a given object, makes an empty, which moves to indicate the velocity (displacement between frames) for each axis of the original object
# and another empty providing magnitude of the movement
# and another empty providing accumulative displacement for each axis, 
# and yet another for acceleration. 
# To run, select object, generate a motion path for the object (Editor>Motion Paths) across the total desired frame range, and Run Script.
# Bret Battey / BatHatMedia.com Dec 2015
# ---------

import bpy
from bpy.props import *
from mathutils import *
from math import *
import time

# ----- FUNCTION DEFINITIONS -----

# Time utils thanks to http://blenderscripting.blogspot.co.uk/search/label/time

def get_last_time():  
    if len(time_list) < 2:  
        return "ERROR: must have two time entries to calculate the difference"  
    return time_list[-1] - time_list[-2]      
  
def mark_time():  
    time_list.append(time.time())      
  
time_list = [] 


# -------------  SCRIPT START

object = bpy.context.object
path = object.motion_path
frame_start = path.frame_start
frame_end = path.frame_end
path_length = path.length
# velocity empty
bpy.ops.object.empty_add(type='PLAIN_AXES', radius=1, view_align=False, location=(0,0,0), layers=object.layers)
velempty = bpy.context.object
velempty.name = object.name + '_velocity'
# vector magnitude 
bpy.ops.object.empty_add(type='PLAIN_AXES', radius=1, view_align=False, location=(0,0,0), layers=object.layers)
magempty = bpy.context.object
magempty.name = object.name + '_magnitude'
# accumulative distance traveled 
bpy.ops.object.empty_add(type='PLAIN_AXES', radius=1, view_align=False, location=(0,0,0), layers=object.layers)
accumempty = bpy.context.object
accumempty.name = object.name + '_accum_distance'
# acceleration 
bpy.ops.object.empty_add(type='PLAIN_AXES', radius=1, view_align=False, location=(0,0,0), layers=object.layers)
accelempty = bpy.context.object
accelempty.name = object.name + '_acceleration'

mark_time() # start measuring elapsed time

for i in range(0,path_length-2): # motion path index
    nextPointCo = path.points[i+1].co # next time point
    thisPointCo = path.points[i].co # this time point
    dif = nextPointCo-thisPointCo # x,y,z distances
    mag = sqrt(dif[0]*dif[0]+dif[1]*dif[1]+dif[2]*dif[2]) # vector magnitude via pythagorean theorem
    velempty.location = dif # set vel empty's location to the velocity vector
    magempty.location[2] = mag # set z position of mag empty's location to the magnitude
    frame = frame_start+i  # get absolute frame number
    velempty.keyframe_insert(data_path='location',frame=frame,index=-1) # set keyframe for velocity. index -1 = set all three axes
    magempty.keyframe_insert(data_path='location',frame=frame,index=2) # set keyframe for velocity. index -1 = set all three axes
    if i == 0:
        accumempty.keyframe_insert(data_path='location',frame=frame,index=-1) # set keyframe. index -1 = set all three axes
        accumdif = accumempty.location # start accumulated distance with a convenient 0,0,0 vector
    else:
        accumdif[0] += abs(dif[0]) # add absolute value of velocity to the accumulative distance
        accumdif[1] += abs(dif[1])
        accumdif[2] += abs(dif[2])
        accumempty.location = accumdif # move empty to this point
        accumempty.keyframe_insert(data_path='location',frame=frame,index=-1) # set keyframe. index -1 = set all three axes
        accelempty.location = dif-lastdif # acceleration = dif between this frame's velocity and the previous frame's
        accelempty.keyframe_insert(data_path='location',frame=frame-1,index=-1) # set keyframe ONE FRAME BACK. index -1 = set all three axes
    lastdif = dif

   
mark_time()

print('The keyframing took {: 5g} seconds'.format(get_last_time()))



No comments:

Post a Comment