""" Kirby Urner 4D Solutions First published: June 12 2007 Simple framework for studying X3D, an industry standard inheriting from VRML, plus use of Template class in the Standard Library string module. Dependencies (outside of Standard Library): http://www.4dsolutions.net/ocn/python/stickworks.py http://www.4dsolutions.net/ocn/python/polyhedra.py """ from string import Template from time import asctime, localtime, time from random import randint from stickworks import Vector, Edge from polyhedra import Tetrahedron, Cube, Icosahedron, Octahedron, Coupler, Mite from math import sqrt, hypot, acos gl_thetemplate = Template( """ $thescene """ ) gl_shape = Template(""" $shapes """ ) gl_theedge = Template( """ """ ) gl_thevertex = Template ( """ """ ) gl_theface = Template ( """ """ ) colordict = dict( Red = "1.0 0.0 0.0", Green = "0.0 1.0 0.0", Blue = "0.0 0.0 1.0", Yellow = "1.0 1.0 0.0", Cyan = "0.0 1.0 1.0", Magenta = "1.0 0.0 1.0", White = "1.0 1.0 1.0", Black = "0.0 0.0 0.0", Orange = "1.0 0.5 0.0", Violet = "0.309804 0.184314 0.309804", Indigo = "0.294117 0.0 0.509803", Brown = "0.647059 0.164706 0.164706", Wood = "0.647059 0.164706 0.164706", Grey = "0.752941 0.752941 0.752941", Gray = "0.752941 0.752941 0.752941", Gold = "0.8 0.498039 0.196078", Silver = "0.90 0.91 0.98") class Scene (object) : thepath = 'c:/python25/Lib/site-packages/' def __init__(self, thefile='test.x3d', desc = 'test file', author = 'me'): self.header = dict( filename = thefile, thedescript = desc, thedate = asctime(localtime(time())), theauthor = author) self.objects = [] def _orient(self, etuple): """ recycled code from a much earlier VRML2 application, used in proprietary mode for DST to produce StrangeAttractors .wrl files. """ data = [(0,0,0),0,0,0] # midpoint of edge between vert0 and vert1 midpoint = (etuple[0]+etuple[1])*(1/2.0) # used to translate from origin data[0] = midpoint.xyz # tip = translated endpoint (w/ midpoint at 0,0,0) tip = etuple[1]-midpoint # length of cylinder hyp = tip.length data[1]=2*hyp # height above Y plane xzhyp = hypot(tip.xyz[0],tip.xyz[2]) # rotate zx plane around north/south (y) axis to # have xy plane intersect tip if xzhyp!=0: mval = tip.xyz[2]/xzhyp data[2] = acos(mval) if tip.xyz[0]<=0: data[2] = -data[2] # rotate about x axis, tilting y axis to tip if hyp!=0: mval = tip.xyz[1]/hyp data[3] = acos(mval) return data #object handle for array w/ new data def _edges(self, someobj): # cylinders thevertices = someobj.vertices for edge in someobj.edges: v1 = edge.v0 v2 = edge.v1 vrmldata = self._orient((v1,v2)) length,roty,rotx = (vrmldata[1],vrmldata[2],vrmldata[3]) edict = dict( radius = edge.radius, color = colordict[someobj.ecolor], length = length, translate = "%r %r %r" % vrmldata[0], roty = "0 1 0 %s" % roty, rotx = "1 0 0 %s" % rotx) someobj.text = someobj.text + gl_theedge.substitute(edict) def _vertexes(self, someobj): # spheres thevertices = someobj.vertices for vertex in someobj.vertices: sdict = dict( color = colordict[someobj.vcolor], radius = thevertices[vertex].radius, translate = "%r %r %r" % thevertices[vertex].xyz) someobj.text = someobj.text + gl_thevertex.substitute(sdict) def _faces(self, someobj): # polygons thevertices = someobj.vertices for face in someobj.faces: theverts = " " for v in face: theverts += " %r %r %r, " % thevertices[v].xyz fdict = dict( color = colordict[someobj.fcolor], coords = theverts, corners = range(len(face)) + [-1]) someobj.text = someobj.text + gl_theface.substitute(fdict) def write(self): # set the stage self.fileobject = open(Scene.thepath + self.header['filename'], 'w') # write each object for obj in self.objects: obj.text = "" # patch of xml for each object if obj.showvertices: self._vertexes(obj) if obj.showedges: self._edges(obj) if obj.showfaces: self._faces(obj) shapes = "" # concatenate the xml for obj in self.objects: shapes = shapes + obj.text # save the whole scene, telescoping back to gl_thetemplate # as outer context self.header["thescene"] = shapes self.fileobject.write(gl_thetemplate.substitute(self.header)) self.fileobject.close() # TESTS """ An interesting feature of this code is the used of attributes not mentioned anywhere in the class definition. Python allows generic objects to be assigned new properties at will. So XML builds up within objects as obj.text, before going to a file as a final step. Likewise, color attributes fcolor, vcolor and ecolor are expected for each polyhedron. """ def makecoupler(): thecube = Cube() thecube.ecolor = "Green" thecube.vcolor = "Silver" thecube.showfaces = False thecoupler = Coupler() thecoupler.ecolor = "Orange" thecoupler.vcolor = "Silver" thecoupler.fcolor = "Wood" output = Scene('test0.x3d') output.objects.append(thecube) output.objects.append(thecoupler) output.write() def makemite(): thecube = Cube() thecube.ecolor = "Green" thecube.vcolor = "Silver" thecube.showfaces = False thecoupler = Coupler() thecoupler.showfaces = False thecoupler.ecolor = "Orange" thecoupler.vcolor = "Silver" themite = Mite() themite.ecolor = "Orange" themite.vcolor = "Silver" output = Scene('test1.x3d') output.objects.append(thecube) output.objects.append(thecoupler) output.objects.append(themite) output.write() def maketent(): output = Scene('test2.x3d') # naming disk file Vector.radius = 0.07 thetet = Tetrahedron() thetet.showfaces = False thetet.ecolor = "Yellow" thetet.vcolor = "Indigo" output.objects.append(thetet) # appending Polyhedron object output.write() def makeicosa(): output = Scene('test3.x3d') # naming disk file icosa = Icosahedron() * sqrt(2) icosa.fcolor = "Cyan" icosa.vcolor = "Silver" icosa.ecolor = "Orange" # appending a scaled Polyhedron object output.objects.append( icosa ) output.write() def manymes(): pass def test(): """list the functions""" thetests = [ makecoupler, # Coupler makemite, # Mighty Mite maketent, # tetra tent makeicosa, # i, icosa manymes] # many mes while True: print """ Choose: 0 Coupler 1 Mighty Mite 2 Tetra Tent 3 I, Icosa 4 Many Mes Q Outta here! """ ans = raw_input('Choice? ') if ans in 'Qq': break # trap more errors here thetests[int(ans)]() # perform user selection (or crash?) print "View output, hit Enter to continue..." # pause to look in the POV-Ray window ok = raw_input() return # null if __name__ == '__main__': test()