Marcus H. Mendenhall:
|>Also, how do you take a vertex list and turn it into a polygon (face,
|>loop, edges) inside a DX network?
|>
|>I found Connect, but it triangulates the area.
|
|If you don't _absolutely_ need to do it in DX, I can send you a
|python package, which is quite short and simple, which generates FLE
|objects for DX. It is a kind of minimal package for writing DX files
|and for easily converting a data into the right structure. Inside
|DX, it takes significant juggling (although the algorithm I use in
|python could probably be done).
Sure, I'd be interested in a copy.
Thanks,OK, I'll post this to the list since it isn't really too
long... a quick & dirty python package for writing DX native files
and, especially, FLE objects. here it is (as an attachment so that
lines don't get wrapped). If anyone (who wants this) has trouble
receiving this via the list, I will send it directly.
Marcus Mendenhall
from math import *
from Numeric import *
import Numeric
import struct
class dxfile:
def __init__(self, filename, mode="ascii"):
self.mode=mode
self.binary_data_offset=0
self.file=open(filename,"w")
self.binary_data=""
self.typedict={"int":Numeric.Int32,"float":Numeric.Float32,"unsigned
byte":Numeric.UnsignedInt8,\
"byte":Numeric.Int8, "short":Numeric.Int16,
"complex":Numeric.Complex32}
def print_array(self, name, type, rank, formatstr, data):
print >> self.file, "#"
if rank==1:
shapestr="shape %d"%len(data[0])
elif rank==0:
shapestr=""
else:
raise "don't know how to print arrays beyond rank 1"
if self.mode=="ascii":
print >> self.file, 'object "%s" class array type %s rank
%d %s items %d data follows'\
%(name, type, rank, shapestr, len(data))
for i in tuple(data):
if rank==0:
print >> self.file,formatstr%i
else:
print >> self.file, formatstr%tuple(i)
elif self.mode=="binary":
#determine if we are big-or-little endian, and write file
as native for this machine
endian=struct.unpack("i","\0\0\0\1")
if endian[0]==1:
endstr="msb"
else:
endstr="lsb"
print >> self.file, 'object "%s" class array type %s rank
%d %s items %d %s ieee data %d'\
%(name, type, rank, shapestr, len(data), endstr,
len(self.binary_data))
self.binary_data+=array(data).astype(self.typedict[type]).tostring()
def print_FLE_object(self, basename, faces, data, datatype,
dataformat, base_data_index=0):
"""facelist consists of a list, each element of which
consists of a list of loops which comprise the face.
For Example, a list (((1,2,3),),((2,3,4),),) has two faces,
one of which is represented by positions object elements 1,2 and 3,
and the second of which is represented by positions 2, 3, and
4, so these
two faces share an edge along 2,3. Beware the 1-item lists which
often appear in the innermost levels here, and require the extra comma
to make sure a list is created. Multiple-item lists in the
middle would be used for
faces with holes, as permitted by the dx FLE spec.
Also, note that the programmer is responsible for writing out
the actual positions list
to which these objects refer. Since one positions list may be
a concatenation
of a number of FLE objects, base_data_index can be used to
offset all the indices
computed in this code to the real start of the object in the
positions."""
edgelist=[]
looplist=[]
facelist=[]
for f in faces:
facelist.append(len(looplist)+base_data_index) #this is
where this face begins
for l in f:
looplist.append(len(edgelist)+base_data_index)
for i in l:
edgelist.append(i+base_data_index)
self.print_array(basename+"_edges", "int", 0, "%d", edgelist)
print >> self.file, 'attribute "ref" string "positions"'
self.print_array(basename+"_loops", "int", 0, "%d", looplist)
print >> self.file, 'attribute "ref" string "edges"'
self.print_array(basename+"_faces", "int", 0, "%d", facelist)
print >> self.file, 'attribute "ref" string "loops"'
self.print_array(basename+"_data",datatype, 0, dataformat, data)
print >> self.file, 'attribute "dep" string "faces"'
print >> self.file, "#"
print >> self.file, 'object "%s" class field' % basename
print >> self.file, 'component "edges" "%s"' % (basename+"_edges")
print >> self.file, 'component "data" "%s"' % (basename+"_data")
print >> self.file, 'component "loops" "%s"' % (basename+"_loops")
print >> self.file, 'component "faces" "%s"' % (basename+"_faces")
def print_line(self, string):
print >> self.file, string
def close(self):
self.file.write("end\n"+self.binary_data)
self.file.close()
def csd(theta):
return cos(theta*pi/180.0), sin(theta*pi/180.0)
def extrude_poly(poly_data, poly_origin, geometries, cap_ends=0, close_loop=0):
""" takes a single polygon, presumed planar, and creates the
appropriate list of faces to
connect it to a set of points such that the point poly_origin is
translated to each of the centers,
then rotated by euler angles theta, eta, phi, (theta=polar
angle:0=north, eta=longitude, phi=rotation),
then scaled by scale, and then connected to the
previous surface where the center, rotation, and scale are in the
elements of the geometries list.
The angles in geometries are specified in degrees"""
poslist=[]
faces=[]
poly_array=array(poly_data,Float)
sides=len(poly_data)
slices=len(geometries)
xyz0=array(poly_origin,Float)
poly_array[:]=poly_array[:]-xyz0 #translate to origin
poly_array=transpose(poly_array) #transposed array allows dot to
transform all coordinates at once
for (center, euler, scale) in geometries:
c,s=csd(euler[2])
phimat=array(((c,s,0),(-s,c,0),(0,0,1)),Float)
c,s=csd(euler[0])
thetamat=array(((c,0,s),(0,1,0),(-s,0,c)),Float)
c,s=csd(euler[1])
etamat=array(((c,s,0),(-s,c,0),(0,0,1)),Float)
emat=dot(etamat,dot(thetamat,phimat))*scale
pt=transpose(dot(emat, poly_array))+center
ptl=list(pt)
for i in ptl:
poslist.append(tuple(i))
#if the structure is to be closed, connect the last poly back to
the first, otherwise skip this
if close_loop: connections=slices
else: connections=slices-1
#index through the rotated polygons, and connect corresponding
edges with triangle pairs (since they may not be planar)
for i in range(connections):
nextslice=(i+1)%slices
base1=i*sides
base2=nextslice*sides
for j in range(sides):
corner1=base1+j
corner2=base1+((j+1)%sides)
corner3=base2+j
corner4=base2+((j-1)%sides)
faces.append(((corner1,corner2,corner3),))
faces.append(((corner1,corner3,corner4),))
if not(close_loop) and cap_ends: #if the object has ends, put
caps on them if requested
endloop=range(0,sides)
faces.append((endloop,))
endloop=range((slices-1)*sides,slices*sides)
faces.append((endloop,))
#print >> sys.stderr, poslist, faces
return poslist, faces
if __name__ == '__main__':
def dxfile_test():
dxf=dxfile("test.dx","binary")
try:
q=array((1,2,3,4,5,6,7,8,9,10))
dxf.print_array("data","int",0,"%d",q)
dxf.print_array("data2","float",0,"%f",q)
finally:
dxf.close()
def fancytest():
starpoints=6
loopsteps=1000 #total sections
loopturns=3.5
looppitch=5.0 #units/turn
looptwist=0.5 #degrees/section
loopradius=3.0
loopstepdtheta=2*pi*loopturns/loopsteps
stepsperturn=float(loopsteps)/loopturns
dtheta=pi/starpoints
poly=[]
for i in range(2*starpoints):
r=(2+(i%2))/3.0
theta=i*dtheta
poly.append((r*cos(theta),r*sin(theta),0.0))
center=(0,0,0)
geometries=[]
for i in range(loopsteps):
theta=i*loopstepdtheta
x=cos(theta)*loopradius
z=sin(theta)*loopradius
geometries.append(((x,looppitch*i/stepsperturn,
z),(-theta*180/pi,0,looptwist*i), exp(-i*.001) ))
poslist, faces=extrude_poly(poly, center, geometries,
cap_ends=1, close_loop=0)
file=dxfile("dxtest.dx","binary")
try:
file.print_array("position list","float", 1, "%.3f %.3f
%.3f", poslist)
file.print_FLE_object("default", faces,
range(len(faces)), "int", "%d")
file.print_line('component "positions" "position list"')
finally:
file.close()
fancytest()