The Blender API can be used in Python scripts to automate tasks and read in scientific data. The API allows the user to create and manipulate objects and data, edit properties and groupings, and control the animation sequence of a scientific visualization.
The Blender scripting interface is shown in figure . When a new script is started, a ‘Run Script’ button appears in the GUI—pressing this button will run the script. In addition, Blender scripts can be run from the command line via:
Figure 7.1. The editor and command line allow the user to control the Blender interface and functions via the Blender Python API.
blender - -python myscript.py
The following libraries will be commonly used with Blender scripts for scientific visualization. These typically will appear in the script preamble with ‘import’.
bpy. Application modules that allow control over the context, data types, operators and GUI applications.
bmesh. A standalone module giving access to mesh objects, vertices and connecting segments.
csv. A useful Python module for reading formatted data.
glob. A standard Python module for traversing directory structure and obtaining lists of files to load.
os. An operating system interface useful for loading/finding paths where required libraries might be needed.
math. Basic mathematical operations and functions.
There are many different methods for reading formatted text into a Python dictionary. We employ dictionaries here for their clarity and convenience in terms of defining a Python data construct. The data for this short example will be in the following format:
#Name X Y Z
Point001 4.2 2.3 5.6
Point002 1.2 9.3 6.4
Point003 4.3 2.3 7.4
...
We can then read in the specified data with the following short Python script.
import bpy
import bmesh
import csv
'''Read in the data according to the defined list of fields
Remove the final header if needed.
Define the table as an empty list
'''
fields = ['Name', 'X', 'Y', 'Z']
csvin = csv.reader(open(f))
data=[row for row in csvin]
header=data.pop(0)
table = []
'''For each row in the data list, map the fields and
data elements to a dictionary
Append the dictionary to the table list.
'''
for row in data:
datarow = map(float,row[0].split())
rowdict=dict(zip(fields, datarow))
table.append(rowdict)
For this example we create a cube object mesh (actually, any mesh will work) and remove all but one vertex. That vertex will then be duplicated at all the positions of the imported ASCII text file.
import bpy
import bmesh
import csv
#Switch to edit mode
bpy.ops.object.mode_set(mode = 'EDIT')
obj = bpy.data.objects['Cube']
mesh = obj.data
bm = bmesh.from_edit_mesh(obj.data)
rowcount = 0
for i in range(0,len(bm.verts)-1):
try:
bm.verts[i].co.x = table[rowcount]['X']
bm.verts[i].co.y = table[rowcount]['Y']
bm.verts[i].co.z = table[rowcount]['Z']
rowcount += 1
bmesh.update_edit_mesh(obj.data)
except:
pass
For simulations where the data may be a changing 3D time-series, inserting keyframes between each time step can animate the data. Keyframe insertion and the animation playback toolbar can also be controlled via the Blender API. This example reads in a series of formatted ASCII text files, putting together the previous two code blocks.
import bpy
import bmesh
import csv
import glob
'''This line will change depending on whether Linux,
Mac OS, or Windows is used
'''
files = glob.glob(mydirectory + '/*')
for f in files:
#Switch to edit mode
bpy.ops.object.mode_set(mode = 'EDIT')
print(Frame: + str(f))
bpy.context.scene.frame_set(framecount)
fields = ['Name', 'X', 'Y', 'Z']
csvin = csv.reader(open(f))
data=[row for row in csvin]
header=data.pop(0)
table = []
for row in data:
datarow = map(float,row[0].split())
rowdict=dict(zip(fields, datarow))
table.append(rowdict)
obj = bpy.data.objects['Cube']
mesh = obj.data
bm = bmesh.from_edit_mesh(obj.data)
rowcount = 0
for i in range(0,len(bm.verts)-1):
try:
bm.verts[i].co.x = table[rowcount]['X']
bm.verts[i].co.y = table[rowcount]['Y']
bm.verts[i].co.z = table[rowcount]['Z']
rowcount += 1
bmesh.update_edit_mesh(obj.data)
except:
pass
bpy.ops.anim.keyframe_insert(type='Location')