Copy object to face, edge, or vertex script (updated to 2.5.3.1)

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,

http://fc08.deviantart.net/fs70/f/2010/215/8/7/Copy_2_blender_script_example_by_elfnor.png


"""
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.

cheers elfnor

2 Likes

great job, looks nice & useful.

Hi elfnor!

Can you update your script to actual blender version ?
verts => vertices
and more

Very useful scripsts lately, Thanx elfnor!
But i doesnot work on the latest builds.

Any news on this?

Thanks everyone for the interest.

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

I’ve posted both versions here http://elfnor.deviantart.com/art/Copy-2-Blender-Python-Script-177632885.
and will post any more updates I do there also.

elfnor

Hi i apologize for bumping this old thread but this script is very useful and it was obsolete
so i updated it to latest.:smiley: 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

Looks nice but it needs a license and bl_info. :slight_smile:

Yeah Elfnor did not add any license …this can become good addon i think.

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.

There is a new update for this script https://github.com/elfnor/copy2_blender_addon , but i have problems to enable this script with blender 2.71 or higher , can someone try this

That’s a funny typo in the script, easy to correct. Open script in text editor window, in the beginning, find the line

"warning": ""

(line 27 to be exact)
and add comma at the end, save, install, run.

Thanks eppo after changing the code ( line 27 ) i can enable the addon and run the script . Thanks :eyebrowlift2:

Can someone update this script for blender 2.8 :smiley:

Thanks for asking, I’ll have a look at how difficult this is in the next few days.

Could you show us what you’re using it for.

elfnor

Thanks ,

image

That looks good.

I’ve got a 2.8 version over on github

Not thoroughly tested but works on my install

I hope its useful

E

6 Likes

Hmm, i have some problems maybe they have updated something

Try again, I’ve updated for 2.80.61. They’re still changing the API.

2 Likes

Excellent , Works with the new update For Blender 2.80.61 :smiley::smiley: