I’m learning Blender Python 2.5 and here’s my first real script. It copies an object to the selected vertexes, edges or faces of another object. Use is explained in the comments.
Here’s a quick example of the the type of thing you can build with it. The base object is an icosphere with subdivisons set to 1. The spike is an edited cone and was copied to the vertexes, the torus was copied to the faces and the tube to the edges,
"""
EFH 1/08/10 Updated to Blender 2.53.0 Beta
script to copy a selected object to the selected vertexs, edges or faces of the active object
To use:
select object to copy from - RMB
select object to copy to - SHIFT RMB
enter edit mode - TAB
choose desired select mode - CTRL TAB face or edge or vertex
select several faces, edges or verts - RMB, SHIFT RMB
exit edit mode - TAB
run script - ALT P
The choosen select mode will determine if the selected object is copied to
the vertex, edge, or face of the active object.
For face select mode:
The z axis of the copy from will be aligned with the face normal.
The x axis of the copy from will point toward one of the face vertexs.
For edge select mode:
The x axis of the copy from will be aligned along the edge.
The z axis of the copy from will be the average of the edge's vertex normals.
For vertex select mode:
The z axis of the copy from will be aligned with the vertex normal.
The x axis of the copy from will point along an edge.
Any scaling or rotation of the source_obj mesh to the desired orientation to the
x and z axes has to be done in edit mode. Also in edit mode the source_obj mesh can be moved
realative to the pivot point. This will offset the copies relative to the face centers.
"""
import bpy
import mathutils
def copyto(source_obj, pos, xdir, zdir):
"""
copy the source_obj to pos, so its z-axis points in zdir and its x-axis
points in xdir
"""
#select only source_obj
bpy.ops.object.select_all(action='DESELECT')
source_obj.select = True
#duplicate source_obj
bpy.ops.object.duplicate()
#and leaves copy selected but not active
copy_obj = bpy.context.selected_objects[0]
xdir = xdir.normalize()
zdir = zdir.normalize()
#rotation first
z_axis = zdir
x_axis = xdir
y_axis = z_axis.cross(x_axis)
rot_mat = mathutils.Matrix(x_axis,y_axis,z_axis)
rot_mat = rot_mat.to_4x4()
copy_obj.matrix_world = rot_mat
#move object onto face
copy_obj.location = pos
return copy_obj
#need to be in object mode for script to work
bpy.ops.object.mode_set(mode='OBJECT')
sel_faces = []
sel_verts = []
sel_edges = []
copy_list = []
obj = bpy.context.active_object
#objects are not in selection order
#for now find a selected object that isn't active object
#if more than two objects are selected should do something else
for s_obj in bpy.context.selected_objects:
if s_obj != obj:
source_obj = s_obj
if bpy.context.tool_settings.mesh_selection_mode[0] == True:
#vertex select mode
for v in obj.data.verts:
if v.select == True:
sel_verts.append(v)
#make a set for each vertex. The set contains all the connected verts
#use sets so the list is unique
vert_con = [set() for i in range(len(obj.data.verts))]
for e in obj.data.edges:
vert_con[e.verts[0]].add(e.verts[1])
vert_con[e.verts[1]].add(e.verts[0])
for v in sel_verts:
pos = obj.matrix_world * v.co
xco = obj.matrix_world * obj.data.verts
[list(vert_con[v.index])[0]].co
zdir = obj.matrix_world * (v.co + v.normal) - pos
zdir = zdir.normalize()
edir = pos - xco
#edir is nor perpendicular to z dir
#want xdir to be projection of edir onto plane through pos with direction zdir
xdir = edir - edir.dot(zdir) * zdir
xdir = -xdir.normalize()
copy = copyto(source_obj, pos, xdir, zdir)
copy_list.append(copy)
if bpy.context.tool_settings.mesh_selection_mode[1] == True:
#edge select mode
for e in obj.data.edges:
if e.select == True:
sel_edges.append(e)
for e in sel_edges:
#pos is average of two edge vertexs
v0 = obj.matrix_world * obj.data.verts[e.verts[0]].co
v1 = obj.matrix_world * obj.data.verts[e.verts[1]].co
pos = (v0 + v1)/2
#xdir is along edge
xdir = v0-v1
xdir = xdir.normalize()
#project each edge vertex normal onto plane normal to xdir
vn0 = obj.matrix_world * (obj.data.verts[e.verts[0]].co + obj.data.verts[e.verts[0]].normal) - v0
vn1 = obj.matrix_world * (obj.data.verts[e.verts[1]].co + obj.data.verts[e.verts[1]].normal) - v1
vn0p = vn0 - vn0.dot(xdir)*xdir
vn1p = vn1 - vn1.dot(xdir)*xdir
#the mean of the two projected normals is the zdir
zdir = vn0p + vn1p
zdir = zdir.normalize()
copy = copyto(source_obj, pos, xdir, zdir)
copy_list.append(copy)
if bpy.context.tool_settings.mesh_selection_mode[2] == True:
#face select mode
for f in obj.data.faces:
if f.select == True:
sel_faces.append(f)
for f in sel_faces:
#get face center of transformed object
fco = obj.matrix_world * f.center
#get first vertex corner of transformed object
vco = obj.matrix_world * obj.data.verts[f.verts[0]].co
#get face normal of transformed object
fn = obj.matrix_world*(f.center + f.normal) - fco
fn = fn.normalize()
copy = copyto(source_obj, fco, vco - fco, fn)
copy_list.append(copy)
#select all copied objects
for copy in copy_list:
copy.select = True
I hope it’s useful or helps someone with learning blender python.
Here’s the script updated for 2.5.3.1. I tested it on r31458.
I just needed to replace vert -> vertices and mesh_selection_mode -> mesh_select_mode
"""
EFH 31/08/10 Updated to Blender 2.53.1 r31458
script to copy a selected object to the selected vertices, edges or faces of the active object
To use:
select object to copy from - RMB
select object to copy to - SHIFT RMB
enter edit mode - TAB
choose desired select mode - CTRL TAB face or edge or vertex
select several faces, edges or vertices - RMB, SHIFT RMB
exit edit mode - TAB
run script - ALT P
The choosen select mode will determine if the selected object is copied to
the vertex, edge, or face of the active object.
For face select mode:
The z axis of the copy from will be aligned with the face normal.
The x axis of the copy from will point toward one of the face vertexs.
For edge select mode:
The x axis of the copy from will be aligned along the edge.
The z axis of the copy from will be the average of the edge's vertex normals.
For vertex select mode:
The z axis of the copy from will be aligned with the vertex normal.
The x axis of the copy from will point along an edge.
Any scaling or rotation of the source_obj mesh to the desired orientation to the
x and z axes has to be done in edit mode. Also in edit mode the source_obj mesh can be moved
realative to the pivot point. This will offset the copies relative to the face centers.
"""
import bpy
import mathutils
def copyto(source_obj, pos, xdir, zdir):
"""
copy the source_obj to pos, so its z-axis points in zdir and its x-axis
points in xdir
"""
#select only source_obj
bpy.ops.object.select_all(action='DESELECT')
source_obj.select = True
#duplicate source_obj
bpy.ops.object.duplicate()
#and leaves copy selected but not active
copy_obj = bpy.context.selected_objects[0]
xdir = xdir.normalize()
zdir = zdir.normalize()
#rotation first
z_axis = zdir
x_axis = xdir
y_axis = z_axis.cross(x_axis)
rot_mat = mathutils.Matrix(x_axis,y_axis,z_axis)
rot_mat = rot_mat.to_4x4()
copy_obj.matrix_world = rot_mat
#move object onto face
copy_obj.location = pos
return copy_obj
#need to be in object mode for script to work
bpy.ops.object.mode_set(mode='OBJECT')
sel_faces = []
sel_verts = []
sel_edges = []
copy_list = []
obj = bpy.context.active_object
#objects are not in selection order
#for now find a selected object that isn't active object
#if more than two objects are selected should do something else
for s_obj in bpy.context.selected_objects:
if s_obj != obj:
source_obj = s_obj
if bpy.context.tool_settings.mesh_select_mode[0] == True:
#vertex select mode
for v in obj.data.vertices:
if v.select == True:
sel_verts.append(v)
#make a set for each vertex. The set contains all the connected vertices
#use sets so the list is unique
vert_con = [set() for i in range(len(obj.data.vertices))]
for e in obj.data.edges:
vert_con[e.vertices[0]].add(e.vertices[1])
vert_con[e.vertices[1]].add(e.vertices[0])
for v in sel_verts:
pos = obj.matrix_world * v.co
xco = obj.matrix_world * obj.data.vertices
[list(vert_con[v.index])[0]].co
zdir = obj.matrix_world * (v.co + v.normal) - pos
zdir = zdir.normalize()
edir = pos - xco
#edir is nor perpendicular to z dir
#want xdir to be projection of edir onto plane through pos with direction zdir
xdir = edir - edir.dot(zdir) * zdir
xdir = -xdir.normalize()
copy = copyto(source_obj, pos, xdir, zdir)
copy_list.append(copy)
if bpy.context.tool_settings.mesh_select_mode[1] == True:
#edge select mode
for e in obj.data.edges:
if e.select == True:
sel_edges.append(e)
for e in sel_edges:
#pos is average of two edge vertexs
v0 = obj.matrix_world * obj.data.vertices[e.vertices[0]].co
v1 = obj.matrix_world * obj.data.vertices[e.vertices[1]].co
pos = (v0 + v1)/2
#xdir is along edge
xdir = v0-v1
xdir = xdir.normalize()
#project each edge vertex normal onto plane normal to xdir
vn0 = obj.matrix_world * (obj.data.vertices[e.vertices[0]].co
+ obj.data.vertices[e.vertices[0]].normal) - v0
vn1 = obj.matrix_world * (obj.data.vertices[e.vertices[1]].co
+ obj.data.vertices[e.vertices[1]].normal) - v1
vn0p = vn0 - vn0.dot(xdir)*xdir
vn1p = vn1 - vn1.dot(xdir)*xdir
#the mean of the two projected normals is the zdir
zdir = vn0p + vn1p
zdir = zdir.normalize()
copy = copyto(source_obj, pos, xdir, zdir)
copy_list.append(copy)
if bpy.context.tool_settings.mesh_select_mode[2] == True:
#face select mode
for f in obj.data.faces:
if f.select == True:
sel_faces.append(f)
for f in sel_faces:
#get face center of transformed object
fco = obj.matrix_world * f.center
#get first vertex corner of transformed object
vco = obj.matrix_world * obj.data.vertices[f.vertices[0]].co
#get face normal of transformed object
fn = obj.matrix_world*(f.center + f.normal) - fco
fn = fn.normalize()
copy = copyto(source_obj, fco, vco - fco, fn)
copy_list.append(copy)
#select all copied objects
for copy in copy_list:
copy.select = True
Hi i apologize for bumping this old thread but this script is very useful and it was obsolete
so i updated it to latest. If any one still encounters bugs please post :yes:
#Updated to 2.61 on 17 jan 2012 By Brenel (Original by Author Elfnor)
import bpy
import mathutils
def copyto(source_obj, pos, xdir, zdir):
"""
copy the source_obj to pos, so its z-axis points in zdir and its x-axis
points in xdir
"""
#select only source_obj
bpy.ops.object.select_all(action='DESELECT')
source_obj.select = True
#duplicate source_obj
bpy.ops.object.duplicate()
#and leaves copy selected but not active
copy_obj = bpy.context.selected_objects[0]
xdir.normalize()
zdir.normalize()
#rotation first
z_axis = zdir
x_axis = xdir
y_axis = z_axis.cross(x_axis)
print(x_axis)
print(y_axis)
print(z_axis)
rot_mat = mathutils.Matrix((x_axis,y_axis,z_axis))
rot_mat = rot_mat.to_4x4()
copy_obj.matrix_world = rot_mat
#move object onto face
copy_obj.location = pos
return copy_obj
#need to be in object mode for script to work
bpy.ops.object.mode_set(mode='OBJECT')
sel_faces = []
sel_verts = []
sel_edges = []
copy_list = []
obj = bpy.context.active_object
#objects are not in selection order
#for now find a selected object that isn't active object
#if more than two objects are selected should do something else
for s_obj in bpy.context.selected_objects:
if s_obj != obj:
source_obj = s_obj
if bpy.context.tool_settings.mesh_select_mode[0] == True:
#vertex select mode
for v in obj.data.vertices:
if v.select == True:
sel_verts.append(v)
#make a set for each vertex. The set contains all the connected vertices
#use sets so the list is unique
vert_con = [set() for i in range(len(obj.data.vertices))]
for e in obj.data.edges:
vert_con[e.vertices[0]].add(e.vertices[1])
vert_con[e.vertices[1]].add(e.vertices[0])
for v in sel_verts:
pos = obj.matrix_world * v.co
xco = obj.matrix_world * obj.data.vertices
[list(vert_con[v.index])[0]].co
zdir = obj.matrix_world * (v.co + v.normal) - pos
zdir.normalize()
edir = pos - xco
#edir is nor perpendicular to z dir
#want xdir to be projection of edir onto plane through pos with direction zdir
xdir = edir - edir.dot(zdir) * zdir
xdir.normalize()
copy = copyto(source_obj, pos, xdir, zdir)
copy_list.append(copy)
if bpy.context.tool_settings.mesh_select_mode[1] == True:
#edge select mode
for e in obj.data.edges:
if e.select == True:
sel_edges.append(e)
for e in sel_edges:
#pos is average of two edge vertexs
v0 = obj.matrix_world * obj.data.vertices[e.vertices[0]].co
v1 = obj.matrix_world * obj.data.vertices[e.vertices[1]].co
pos = (v0 + v1)/2
#xdir is along edge
xdir = v0-v1
#print(xdir)
xdir.normalize()
#project each edge vertex normal onto plane normal to xdir
vn0 = obj.matrix_world * (obj.data.vertices[e.vertices[0]].co
+ obj.data.vertices[e.vertices[0]].normal) - v0
vn1 = obj.matrix_world * (obj.data.vertices[e.vertices[1]].co
+ obj.data.vertices[e.vertices[1]].normal) - v1
vn0p = vn0 - vn0.dot(xdir)*xdir
vn1p = vn1 - vn1.dot(xdir)*xdir
#the mean of the two projected normals is the zdir
zdir = vn0p + vn1p
zdir.normalize()
copy = copyto(source_obj, pos, xdir, zdir)
copy_list.append(copy)
if bpy.context.tool_settings.mesh_select_mode[2] == True:
#face select mode
for f in obj.data.faces:
if f.select == True:
sel_faces.append(f)
for f in sel_faces:
#get face center of transformed object
fco = obj.matrix_world * f.center
#get first vertex corner of transformed object
vco = obj.matrix_world * obj.data.vertices[f.vertices[0]].co
#get face normal of transformed object
fn = obj.matrix_world*(f.center + f.normal) - fco
fn.normalize()
copy = copyto(source_obj, fco, vco - fco, fn)
copy_list.append(copy)
#select all copied objects
for copy in copy_list:
copy.select = True
I’ve been looking for something to do this for months. Now I can try to build the Lexx.
Question though. How do I install this script? Where do I get it? An add-on somewhere?
This is such a useful idea, it needs to be trunked (as add-on I guess) ASAP.