This is the final version of the script!!! It now moves the faces, skins/bridges the new and old edges as expected, and deletes the original selected faces. [I’ll leave the original script in here also, so you can see how the script evolved]
EDIT (2007-07-23): added a line to recalculate the newly created face normals. Thanks to Ubuntuist for pointing out this problem.
#!BPY
""" Registration info for Blender menus: <- these words are ignored
Name: 'Expand face loops along face normals (skin+delete)'
Blender: 244
Group: 'Object'
Tip: 'Expands the face loop selection based on the face normal directions (skins and deletes originals)'
"""
__author__ = "René Pihlak aka Daredemo"
__version__ = "1.1.2 2007/07"
__bpydoc__ = """\
"Expand face loops along face normals" moves vertices of selected faces based on the face normal direction.
Usage:
Select some faces and run it in Object mode
"""
import Blender as B
from Blender import Mesh as M
from Blender import Mathutils
from Blender.Mathutils import *
from Blender import *
def deform_norm(o):
f = B.Draw.Create(0.2)
block = []
block.append(("Expansion value ", f, -30.0, 30.0, "Enter extrusion value to move verts based on face normal"))
retval = B.Draw.PupBlock("Expand by", block)
FACT = f.val
v_arr = []
v_arr_s = []
s_arr_f = []
m_mesh = o.getData(mesh=True)
vrt = m_mesh.verts
tot_v = len(m_mesh.verts)
for s_vrt in vrt:
v_arr.append((s_vrt.co, s_vrt.no, (0, 0,)))
s_mesh = [ff for ff in m_mesh.faces if ff.sel]
ft_a = []
f_v_tot = []
f_e_tot = []
#Count the index of the vert in the "old" array
new_count = 0
#Count the new vertices that will be made
new_count2 = 0
for ft, f_val in enumerate(s_mesh):
fv_a = []
fv_f = []
f_v_tot.append(len(f_val.v))
#Finding the vertice pairs that make edges of given faces and adding to an array
for f_key in f_val.edge_keys:
f_e_tot.append(f_key)
for fg in f_val.v:
#Add the data to the "old" array
v_arr_s.append(fg.index)
#Check if the this is the first occurence of this vertex
if new_count == v_arr_s.index(fg.index):
#If it's first occurence, add a new count for it
s_arr_f.append((tot_v+new_count2,fg.index))
new_count2 += 1
else:
#If it's already used vertex, refer to the previous occurence in the "new" array
fbh = [fh[1] for fh in s_arr_f]
s_arr_f.append((s_arr_f[fbh.index(fg.index)][0],fg.index))
new_count += 1
#If an edge is listed more than once, it's in between faces and we'll ignore it
f_e_u = [f_sel for f_sel in f_e_tot if f_e_tot.count(f_sel) == 1]
#list of currently selected faces to delete in the end
F_DEL = m_mesh.faces.selected()
for fc, val in enumerate(m_mesh.faces):
c_mesh = m_mesh.faces[fc]
c_m_no = Vector(c_mesh.no)
if c_mesh.sel:
for vt, v_val in enumerate(c_mesh.v):
v_arr.insert(v_val.index, (v_arr[v_val.index][0], v_arr[v_val.index][1], (v_arr[v_val.index][2][0],1)))
v_arr.pop(v_val.index+1)
v_no = v_val.no
#Find the length of the projection of the vertex normal to the face normal
vt_prj = ProjectVecs(v_val.no, c_m_no)
prj_len = (vt_prj[0]**2 + vt_prj[1]**2 + vt_prj[2]**2)**0.5
if prj_len <> 1:
if v_arr[v_val.index][2][0] == 0:
#If the vertex normal is not identical to face normal, scale the vertice normal
v_arr.insert(v_val.index, (v_arr[v_val.index][0], (v_arr[v_val.index][1][0]/prj_len, v_arr[v_val.index][1][1]/prj_len, v_arr[v_val.index][1][2]/prj_len), (1, v_arr[v_val.index][2][1])))
v_arr.pop(v_val.index+1)
#Extend the vertices
EXTEND = []
face_count = 0
vert_count = 0
for fr in f_v_tot:
for fx in range(vert_count,vert_count+fr):
if fx == v_arr_s.index(v_arr_s[fx]):
a,b,c = v_arr[v_arr_s[fx]][0]
a += v_arr[v_arr_s[fx]][1][0]*FACT
b += v_arr[v_arr_s[fx]][1][1]*FACT
c += v_arr[v_arr_s[fx]][1][2]*FACT
EXTEND.append((a,b,c))
vert_count += fr
m_mesh.verts.extend(EXTEND)
#Extend faces
EXTEND = [fext[0] for fext in s_arr_f]
for fz in f_v_tot:
gg = []
for gx in range(face_count,face_count+fz):
gg.append(EXTEND[gx])
face_count +=fz
m_mesh.faces.extend(gg)
#Extend the edges [filling the caps]
for this_edge in f_e_u:
EXTEND = []
EXTEND.append(int(this_edge[0]))
EXTEND.append(int(this_edge[1]))
old_new = [fer[1] for fer in s_arr_f]
old_v3 = old_new.index(this_edge[1])
old_v4 = old_new.index(this_edge[0])
EXTEND.append(s_arr_f[old_v3][0])
EXTEND.append(s_arr_f[old_v4][0])
#print this_edge[0], this_edge[1]
m_mesh.faces.extend(EXTEND)
#Delete the original selected /faces
m_mesh.faces.delete(1,F_DEL)
#Update the mesh
m_mesh.update()
B.Redraw(-1)
#Recalculate normals
m_mesh.recalcNormals()
if FACT < 0:
m_mesh.flipNormals()
obj = B.Scene.GetCurrent().objects.active
if obj.getData(mesh=1):
deform_norm(obj)
Thought I’ll write a smiple script to move vertices based on the face normals [yes, yes, I know of Alt-S, but it doesn’t work on Mac OS X builds… well, not on PPC… or it works but gives completely ugly results].
Anyway, here’s the script. How do i make the verts move?
import Blender as B from Blender import Mesh as M from Blender import Mathutils from Blender.Mathutils import * from Blender import *
def deform_norm(o):
v_arr = []
m_mesh = o.getData()vrt = m_mesh.verts
for vr, s_vrt in enumerate(vrt):
#making and array with vert coordinates [I want to add extrusion to the script in the end… so I though I might need to know the coordinates, so, never mind the coordinates part], the normals, and two booleans, one is if it’s selected, and the other is if the normal has the same normal as the face
v_arr.append((s_vrt.co, s_vrt.no, (0, 0)))
for fc, val in enumerate(m_mesh.faces):
c_mesh = m_mesh.faces[fc]
c_m_no = Vector(c_mesh.no)
if c_mesh.sel:
for vt, v_val in enumerate(c_mesh.v):
#Changing the “is selected” boolean flag
v_arr.insert(v_val.index, (v_arr[v_val.index][0], v_arr[v_val.index][1], (v_arr[v_val.index][2][0],1)))
#after inserting a new value, I delete the old
v_arr.pop(v_val.index+1)
v_no = v_val.no
vt_prj = ProjectVecs(v_val.no, c_m_no)
#Calculate the projection of the vert normal on the face normal
prj_len = (vt_prj[0]**2 + vt_prj[1]**2 + vt_prj[2]**2)**0.5
if prj_len <> 1:
if v_arr[v_val.index][2][0] == 0:#Avoid writing the same vert data multiple times
v_arr.insert(v_val.index, (v_arr[v_val.index][0], (v_arr[v_val.index][1][0]/prj_len, v_arr[v_val.index][1][1]/prj_len, v_arr[v_val.index][1][2]/prj_len), (1, v_arr[v_val.index][2][1])))
v_arr.pop(v_val.index+1)#Now this should change the coordinates by 0.3 Blender units along the face normal… but…
for ee, v_a in enumerate(v_arr):
if v_a[2][1] == 1:
m_mesh.verts[ee].co[0] = v_a[0][0] + 0.3 * v_a[1][0]
m_mesh.verts[ee].co[1] = v_a[0][1] + 0.3 * v_a[1][1]
m_mesh.verts[ee].co[2] = v_a[0][2] + 0.3 * v_a[1][2]p = (v_a[0][0] + 0.2 * v_a[1][0], v_a[0][1] + 0.2 * v_a[1][1], v_a[0][2] + 0.2 * v_a[1][2])
B.Redraw(-1)
obj = B.Scene.GetCurrent().objects.active
if obj.getData(mesh=1):
deform_norm(obj)