Keyframe Bar Chart Animation Efficiently

Primary

I would like to read in numeric data for multiple objects and create a presentation in Blender similar to a Bar Chart. I am requesting partial assistance, or more, in Python Scripts to automate this startling and critical task. The X scale of a bar should represent the data just as in any bar chart. A sample of the work is included above. I would like an efficient, simple and secure way to achieve my goal.

There are 30 objects each requiring 40 keyframes.

The animated objects move only in one axis (X) so X Scale can be keyframed.

every 20 frames (incrementing by 20, so 0,20,40,60,80 etc),

GIF of my first work https://i.imgur.com/koKg23t.gifv

Secondary

Manually the work is:

• Repeat for 30 Objects.
• Select object A
• Repeat for 40 frame locations
• Scale and Insert Keyframe

(around 40 times for 30 objects), and the risk of making errors is enormous.

• Although your frames (seem) to increment by 20 ... you may want to clarify this, if it is a requirement. The proposed answer below can be changed to incrementing by 20 if necessary – atomicbezierslinger Mar 20 '18 at 8:26
• Thanks atomicbezierslinger my English isn't perfect, yes frames incrementing by 20 starting from frame 0 so 0-20-40 etc – itafan Mar 20 '18 at 9:40
• Suggest only keyframing in the wins and driving the x scale of your bar and the x location of your shield with wins. – batFINGER Mar 20 '18 at 13:12
• @batFINGER wins? – itafan Mar 20 '18 at 13:37
• make a custom property named wins. – batFINGER Mar 20 '18 at 13:51

Here's my approach. I usually find it hard to put the data into lists, so this script uses a text block to get it's input.

import bpy

text_source = 'input' # name of the textblock that contains the data
start_frame = 0 # if you need an offset
step = 20 # space in frames between keyframes
factor = 1.0 # scale all values by this amount

txt = bpy.data.texts[text_source] # getting the text

for a in txt.lines: # iterate through all text lines
if len(a.body) < 2: # if a line is too short (empty lines)
continue # just get the next line
line_list = a.body.split(",") # turn the line into a list
ob_name = line_list[0] # first item is the name
ob_pos = line_list[1:] # sliced, everything BUT the first item
ob = bpy.data.objects[ob_name] # get the object
loc_count = start_frame # set the counter

for single_pos in ob_pos: # iterate through all scale values
ob.scale.x = float(single_pos) * factor # set the x scale for the object
ob.keyframe_insert("scale", 0, loc_count) # add a keyframe, 0 = x, loc_count = which frame
loc_count += step # increase, so the next key frame is set 'step' frames later


'input' has the data in csv style:

ob1,3,5,7,10,2,5,2,5,2,5
ob2,2,4,5,6
ob3,1,2,3,4.5,5,6,7,8,15.5


'factor' can be used to turn 1 meter into 10cm or 10m.

• Thanks! This is perfect, the way I can input the values is just perfect for what I have to do, just changed in line 19 ob.scale.x in ob.dimensions.x – itafan Mar 20 '18 at 23:16
• There is a way to decide in what unit of measure are the inputs processed? If the inputs are: (1,2,5) instead of (1m,2m,5m) it should be (10cm,20cm,50cm) so 1:10 – itafan Mar 20 '18 at 23:28
• @itafan I added two lines. – Haunt_House Mar 20 '18 at 23:42
• @itafan You're welcome. Just make sure you understand the script and not just use it ( : Python is super handy. – Haunt_House Mar 21 '18 at 0:41

Here's how I would write this script for a single object. You could use a "for loop" to iterate through all of your objects.

import bpy

scene = bpy.context.scene
obj = scene.objects['Cube']

y_locations = [
10,
13,
22,
]

current_frame = 0
for y_location in y_locations:
obj.location[1] = y_location
obj.keyframe_insert(
data_path="location", frame=current_frame)

current_frame += 20

• Thanks, this works, my fault I made an error in the request.. – itafan Mar 20 '18 at 22:47

Driving from a custom property.

Further to my comment I thought I would explain how to use one custom property, per club, in this case "points" to drive other objects. Consider this more an exercise in setting up and organizing.

I've used a script below to create the bars and badges, as I find that easier than using the UI.

Quite simply it's a cube as the bar, a sphere as the badge, with origins on their LHS, parented to an empty. The empty has a custom property "points". The scale of the bar, and the location of the badge are driven by the points property of empty. (self.parent["points"])

For convenience added a "League" empty to be able to transform the whole lot.

import bpy
from mathutils import Vector

context = bpy.context
scene = context.scene
mt = context.object
mt.name = name
bar = context.object
bar.name = "%s_bar" % name
o.parent = mt
for v in o.data.vertices:
v.co.x += 1

mt["points"] = 1

d.driver.use_self = True
d.driver.expression = 'self.parent["points"]'
d.driver.use_self = True
d.driver.expression = '%4.2f * self.parent["points"]' % bar.dimensions.x
return mt

clubs = {"Geelong" : [2, 4, 10],
"Richmond" : [2, 3],
"Collingwood" : [0],
"Essendon" : [2, 4, 5],
"Fremantle" : [1, 2],
"Sydney" : [4],
"Brisbane" : [2]}

loc = Vector()
handle = context.object
handle.name = "League"
for name, data in clubs.items():
club.parent = handle
# keyframe data on points

club["points"] = 0.0 # make int for steppy
club.keyframe_insert('["points"]', frame=0)
frame_step = 20
for i, pt in enumerate(data):
club["points"] = float(pt)
club.keyframe_insert('["points"]', frame=(i + 1) * frame_step)

loc.z -= 2.2

scene.objects.active = handle
handle.select = True


Result of running the script. A club can be selected simply from the outliner

• Thanks, as soon as I can I'll try your script, unfortunately a parent's health problems kept me away from the web and blender. Your script seems very very interesting.. thanks again. – itafan Apr 1 '18 at 14:26
• Here is something small to get you started. This answer emphasizes scale keyframes.
• Please Click image to see larger image which is easier to read.

• For beginners in Python note the use of a container, list or tuple, which combines elegantly with [for]. 3 very nearly identical answers use [for].
• Note the origins of each object are aligned on the [Y coordinate] side of cube so that scale works easily.
• You could choose to make a manual selection for each object, that is not what we did here. But we can write a (2-3) lines to iterate on the current selection list
• In this proposed answer the data names the object, this can serve as documentation about your decisions if necessary.
• So above we iterate with (3) objects pictured. The OP will have (30) pieces of data.
• Note the original question was changed to require a different answer.