This is not a direct answer to the question, but rather to the underlying application of the OP.
I occasionally want to display proteins, where the hook method would be terribly slow (calling bpy.ops methods for >10000 bonds...). My current working solution for this specific problem of displaying a bond between two atoms is using constraints, which are much faster to handle with a script.
Here is a (rather lengthy) working example. As a bonus it reuses the data (mesh or beveled curve) for all bonds automatically. So the slow bpy.ops calls only happen once. They could be replaced entirely by creating the base mesh or curve from scratch but are more convenient in this case.
import bpy
import bmesh
def get_bond_data(type='MESH'):
new_bond = None
if type == 'MESH':
data_name = "bond_mesh"
elif type == 'CURVE':
data_name = "bond_curve"
data = bpy.context.blend_data.meshes.get(data_name)
if not data:
# save last selection to restore later
selected = bpy.context.selected_objects
last_active = bpy.context.object
# bond should start at origin and have length 1 in y-direction
if type == 'MESH':
bpy.ops.mesh.primitive_cylinder_add(location=(0,0,0),
depth=1, radius=1.0,
end_fill_type="NOTHING")
new_bond = bpy.context.object
data = new_bond.data
data.name = data_name
# rotate vertices 90 degrees around x, and shift along y axis
bm = bmesh.new()
bm.from_mesh(data)
for vert in bm.verts:
tmp_co = vert.co.copy()
vert.co.y = -tmp_co.z + .5
vert.co.z = tmp_co.y
bm.to_mesh(data)
bm.free()
data.update()
elif type == 'CURVE':
bpy.ops.curve.primitive_bezier_curve_add(location=(0,0,0))
new_bond = bpy.context.object
data = new_bond.data
data.name = data_name
bp = data.splines[0].bezier_points
for i in range(2):
bp[i].co = (0,i,0)
bp[i].handle_left_type = 'VECTOR'
bp[i].handle_right_type = 'VECTOR'
# get bevel object
bevel_name = 'bond_bevel'
bond_bevel = bpy.context.blend_data.objects.get(bevel_name)
if not bond_bevel:
bpy.ops.curve.primitive_bezier_circle_add(radius=1.0, location=(0,0,0))
bond_bevel = bpy.context.object
bond_bevel.name = bevel_name
data.bevel_object = bond_bevel
if new_bond:
# finally delete object and reselect old selection
bpy.context.scene.objects.unlink(new_bond)
for o in selected:
o.select = True
bpy.context.scene.objects.active = last_active
return data
def add_bond(first_atom, second_atom, type='MESH'):
bond_name = "bond_{}-{}".format(first_atom.name, second_atom.name)
bond_data = get_bond_data(type=type)
new_bond = bpy.data.objects.new(bond_name, bond_data)
bpy.context.scene.objects.link(new_bond)
c = new_bond.constraints.new('COPY_LOCATION')
c.name = "from"
c.target = first_atom
c = new_bond.constraints.new('STRETCH_TO')
c.name = "to"
c.rest_length = 1.0
c.volume = 'NO_VOLUME'
c.target = second_atom
# scale bond so it looks better
scale = 0.15
new_bond.scale = (scale, 1.0, scale)
a1 = bpy.context.blend_data.objects.get("atom 1")
a2 = bpy.context.blend_data.objects.get("atom 2")
add_bond(a1, a2, type="CURVE")
#add_bond(a1, a2, type="MESH")