"""
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()