Here is a script I have been working on to sweep a profile along a path. It currently has some problems/limitations but is basically in working order. It will probably be a while until I have time to develop it further.
#####################################################
# PROFILE.py - (c) Neil McAllister April 2003 #
#---------------------------------------------------#
# To use select first the path and then the #
# profile to be swept along the path, then #
# run script with ALT-P. #
# #
# The meshes used have certain restrictions: #
# - they must be planar (i.e. flat) #
# - they must have no faces - only edges #
# - they must make a single path (open or closed)#
# with no branches #
# #
# Currently the profile is assumed to be on the #
# XY plane. #
# #
#####################################################
print "--------------------------------------"
print " Profile script - (c) Neil McAllister"
print "--------------------------------------"
import Blender
from Blender import *
from Blender.Draw import *
from math import *
#####################################################
# Define abort function #
#####################################################
def abort():
print "ABORTING!"
raise Exception, "Error in geometry!"
#####################################################
# Main procedure #
#####################################################
if len(Object.GetSelected())!=2:
print "Script requires exactly 2 meshes selected"
pathobj=Object.GetSelected()[0]
profileobj=Object.GetSelected()[1]
print "Path Object: "+pathobj.name
print "Profile Object: "+profileobj.name
print
######################################################
print "Checking path object..."
path=NMesh.GetRawFromObject(pathobj.name)
pathverts=[]
pathfaces=[]
#####################################################
print "-> Calculating normal...",
x0,y0,z0=path.verts[0].co
x1,y1,z1=path.verts[1].co
x2,y2,z2=path.verts[2].co
pathNx=(y1-y0)*(z2-z0)-(z1-z0)*(y2-y0)
pathNy=(z1-z0)*(x2-x0)-(x1-x0)*(z2-z0)
pathNz=(x1-x0)*(y2-y0)-(y1-y0)*(x2-x0)
Nl = sqrt(pathNx*pathNx+pathNy*pathNy+pathNz*pathNz)
pathNx /= Nl
pathNy /= Nl
pathNz /= Nl
Nx=pathNx
Ny=pathNy
Nz=pathNz
print "DONE"
#####################################################
print "-> Checking if path is planar...",
planar=1
for a in range(2,len(path.verts)):
Vx,Vy,Vz=path.verts[a].co
xnv=abs((y1-y0)*(Vz-z0)-(z1-z0)*(Vy-y0))
ynv=abs((z1-z0)*(Vx-x0)-(x1-x0)*(Vz-z0))
znv=abs((x1-x0)*(Vy-y0)-(y1-y0)*(Vx-x0))
lnv = sqrt(xnv*xnv+ynv*ynv+znv*znv)
if lnv>0:
xnv /=lnv
ynv /= lnv
znv /= lnv
eps=.000001
if (abs(xnv-pathNx)>=eps)&(abs(ynv-pathNy)>=eps)&(abs(znv-pathNz)>=eps):
planar=0
if planar==0:
print "Mesh non-planar"
abort()
else:
print "OK"
#####################################################
print "-> Checking faces...",
onesided=1
for f in path.faces:
if len(f.v)>2:
onesided=0
if onesided==0:
print "Multi-sided faces found"
abort()
else:
print "OK"
print "Path OK"
print
#####################################################
print "Building path data..."
print "-> Building face and vertex list...",
for f in path.faces:
indices=[0,0]
for a in [0,1]:
v=f.v[a]
exists=0
for b in range(0,len(pathverts)):
v2=pathverts[b]
if (v[0]==v2[0])&(v[1]==v2[1])&(v[2]==v2[2]):
exists=1
indices[a]=b
if exists==0:
pathverts.append([v[0],v[1],v[2]])
indices[a]=len(pathverts)-1
pathfaces.append(indices)
print "DONE"
#####################################################
print "-> Building chain...",
pathchain=[]
f=pathfaces.pop(0)
pathchain.append(f[0])
pathchain.append(f[1])
while len(pathfaces)>0:
for a in range(0,len(pathfaces)):
f=pathfaces[a]
if f[0]==pathchain[0]:
pathchain.insert(0,f[1])
pathfaces.pop(a)
break
elif f[0]==pathchain[len(pathchain)-1]:
pathchain.append(f[1])
pathfaces.pop(a)
break
elif f[1]==pathchain[0]:
pathchain.insert(0,f[0])
pathfaces.pop(a)
break
elif f[1]==pathchain[len(pathchain)-1]:
pathchain.append(f[0])
pathfaces.pop(a)
break
pathcyclical=0
if pathchain[0]==pathchain[len(pathchain)-1]:
pathcyclical=1
pathchain.pop()
print "DONE"
print "Path data completed"
print
###########################################################################################################
print "Checking profile object..."
profile=NMesh.GetRawFromObject(profileobj.name)
profileverts=[]
profilefaces=[]
#####################################################
print "-> Calculating normal...",
x0,y0,z0=profile.verts[0].co
x1,y1,z1=profile.verts[1].co
x2,y2,z2=profile.verts[2].co
profileNx=(y1-y0)*(z2-z0)-(z1-z0)*(y2-y0)
profileNy=(z1-z0)*(x2-x0)-(x1-x0)*(z2-z0)
profileNz=(x1-x0)*(y2-y0)-(y1-y0)*(x2-x0)
Nl = sqrt(profileNx*profileNx+profileNy*profileNy+profileNz*profileNz)
profileNx /= Nl
profileNy /= Nl
profileNz /= Nl
print "DONE"
#####################################################
print "-> Checking if profile is planar...",
planar=1
for a in range(2,len(profile.verts)):
Vx,Vy,Vz=profile.verts[a].co
xnv=abs((y1-y0)*(Vz-z0)-(z1-z0)*(Vy-y0))
ynv=abs((z1-z0)*(Vx-x0)-(x1-x0)*(Vz-z0))
znv=abs((x1-x0)*(Vy-y0)-(y1-y0)*(Vx-x0))
lnv = sqrt(xnv*xnv+ynv*ynv+znv*znv)
xnv /= lnv
ynv /= lnv
znv /= lnv
eps=.000001
if (abs(xnv-profileNx)>=eps)&(abs(ynv-profileNy)>=eps)&(abs(znv-profileNz)>=eps):
planar=0
if planar==0:
print "Mesh non-planar"
abort()
else:
print "OK"
#####################################################
print "-> Checking faces...",
onesided=1
for f in profile.faces:
if len(f.v)>2:
onesided=0
if onesided==0:
print "Multi-sided faces found"
abort()
else:
print "OK"
print "Profile OK"
print
#####################################################
print "Building profile data..."
print "-> Building face and vertex list...",
for f in profile.faces:
indices=[0,0]
for a in [0,1]:
v=f.v[a]
exists=0
for b in range(0,len(profileverts)):
v2=profileverts[b]
if (v[0]==v2[0])&(v[1]==v2[1])&(v[2]==v2[2]):
exists=1
indices[a]=b
if exists==0:
profileverts.append([v[0],v[1],v[2]])
indices[a]=len(profileverts)-1
profilefaces.append(indices)
print "DONE"
#####################################################
print "-> Building chain...",
profilechain=[]
f=profilefaces.pop(0)
profilechain.append(f[0])
profilechain.append(f[1])
while len(profilefaces)>0:
for a in range(0,len(profilefaces)):
f=profilefaces[a]
if f[0]==profilechain[0]:
profilechain.insert(0,f[1])
profilefaces.pop(a)
break
elif f[0]==profilechain[len(profilechain)-1]:
profilechain.append(f[1])
profilefaces.pop(a)
break
elif f[1]==profilechain[0]:
profilechain.insert(0,f[0])
profilefaces.pop(a)
break
elif f[1]==profilechain[len(profilechain)-1]:
profilechain.append(f[0])
profilefaces.pop(a)
break
profilecyclical=0
if profilechain[0]==profilechain[len(profilechain)-1]:
profilecyclical=1
profilechain.pop()
print "DONE"
print "Profile data completed"
print
#####################################################
print "Building mesh..."
newMesh=NMesh.GetRaw()
print "-> Creating vertices...",
for a in range(0,len(pathchain)):
va=pathverts[pathchain[a]]
if (a==len(pathchain)-1)&(pathcyclical==0):
vc=pathverts[pathchain[a-1]]
Vx=va[0]-vc[0]
Vy=va[1]-vc[1]
Vz=va[2]-vc[2]
Px=Ny*Vz-Nz*Vy
Py=Nz*Vx-Nx*Vz
Pz=Nx*Vy-Ny*Vx
Pl=sqrt(Px*Px+Py*Py+Pz*Pz)
Px /= Pl
Py /= Pl
Pz /= Pl
elif (a==0)&(pathcyclical==0):
vb=pathverts[pathchain[a+1]]
Vx=vb[0]-va[0]
Vy=vb[1]-va[1]
Vz=vb[2]-va[2]
Px=Ny*Vz-Nz*Vy
Py=Nz*Vx-Nx*Vz
Pz=Nx*Vy-Ny*Vx
Pl=sqrt(Px*Px+Py*Py+Pz*Pz)
Px /= Pl
Py /= Pl
Pz /= Pl
else:
if a==len(pathchain)-1:
vb=pathverts[pathchain[0]]
else:
vb=pathverts[pathchain[a+1]]
if a==0:
vc=pathverts[pathchain[len(pathchain)-1]]
else:
vc=pathverts[pathchain[a-1]]
Vx=vb[0]-va[0]
Vy=vb[1]-va[1]
Vz=vb[2]-va[2]
Px1=Ny*Vz-Nz*Vy
Py1=Nz*Vx-Nx*Vz
Pz1=Nx*Vy-Ny*Vx
Pl=sqrt(Px1*Px1+Py1*Py1+Pz1*Pz1)
Px1 /= Pl
Py1 /= Pl
Pz1 /= Pl
Vx=va[0]-vc[0]
Vy=va[1]-vc[1]
Vz=va[2]-vc[2]
Px2=Ny*Vz-Nz*Vy
Py2=Nz*Vx-Nx*Vz
Pz2=Nx*Vy-Ny*Vx
Pl=sqrt(Px2*Px2+Py2*Py2+Pz2*Pz2)
Px2 /= Pl
Py2 /= Pl
Pz2 /= Pl
Px=Px1+Px2
Py=Py1+Py2
Pz=Pz1+Pz2
theta=acos(Px1*Px2+Py1*Py2+Pz1*Pz2)
Pl=sqrt(Px*Px+Py*Py+Pz*Pz)*cos(theta/2)
Px /= Pl
Py /= Pl
Pz /= Pl
for b in range(0,len(profilechain)):
vp=profileverts[profilechain[b]]
newMesh.verts.append(NMesh.Vert(va[0]+Px*vp[0]+Nx*vp[1], va[1]+Py*vp[0]+Ny*vp[1], va[2]+Pz*vp[0]+Nz*vp[1]))
points=len(profilechain)
print "DONE"
#####################################################
print "Creating faces...",
for a in range(0,len(newMesh.verts)-points,points):
for b in range(0,points-1):
f=NMesh.Face()
f.v.append(newMesh.verts[a+b])
f.v.append(newMesh.verts[a+b+1])
f.v.append(newMesh.verts[a+b+1+points])
f.v.append(newMesh.verts[a+b+points])
newMesh.faces.append(f)
if profilecyclical:
f=NMesh.Face()
f.v.append(newMesh.verts[a+points-1])
f.v.append(newMesh.verts[a])
f.v.append(newMesh.verts[a+points])
f.v.append(newMesh.verts[a+points*2-1])
newMesh.faces.append(f)
if pathcyclical==1:
a+=points
for b in range(0,points-1):
f=NMesh.Face()
f.v.append(newMesh.verts[a+b])
f.v.append(newMesh.verts[a+b+1])
f.v.append(newMesh.verts[b+1])
f.v.append(newMesh.verts[b])
newMesh.faces.append(f)
if profilecyclical:
f=NMesh.Face()
f.v.append(newMesh.verts[a+points-1])
f.v.append(newMesh.verts[a])
f.v.append(newMesh.verts[0])
f.v.append(newMesh.verts[points-1])
newMesh.faces.append(f)
print "DONE"
print "Mesh Completed"
print
#####################################################
NMesh.PutRaw(newMesh,"Test",1)
Redraw()
print "Finished!"
Some problems are it doesn’t copy the location, rotation and scale from the path object so the mesh may land in a strange place. It is also difficult to control the direction of the profile - this is something I need to look into. Another problem is it doesn’t mitre the corners properly when the edges get too short and multiple segments overlap. Despite all the I think it could be of some use.
Neil.