[Edu-sig] Re: VPython
Scott David Daniels
Scott.Daniels at Acm.Org
Mon May 31 16:04:21 EDT 2004
ajsiegel at optonline.net wrote:
> Kirby writes -
>>Yes. I'm meaning to pursue Scott's suggestion to look at Ruth Chabay's
>>code.
> FWIW, PyGeo's copy of Ruth's povexport module extends it a bit, allowing, for example, export of the VPython "faces" (triangle mesh) primitives. The export of the "faces" relies on some functionality not added to Pov-ray until version 3.5.
>
> Art
I looked over the code I thought I had re-done, and it was not
what I remembered working on. Here is a working version that
doesn't build up a massive string, but rather writes to a destination.
If you need a destination, make a CStringIO and use it. It would
be fun to try and mix this with the PyGeo code.
-------------- next part --------------
# ruth chabay, carnegie mellon university (rchabay at andrew.cmu.edu)
# $Id: povexport.py 1.3 2004/05/31 19:56:05 daniels Exp daniels $
# v1.0 2000-12-17
# Markus Gritsch (gritsch at iue.tuwien.ac.at)
# v.1.1 2001-03-09
# *) replaced 'scene' by 'display' everywhere
# *) added spheres at the joints of a curve
# *) consistent pov_texture usage in process_arrow() also for the shaft
# *) ambient light, light sources, up, and fov are now handled correctly
# *) some cosmetic changes to the code
# v.1.1.1 2001-03-22
# *) added 'shadowless' keyword to export()
# Ruth Chabay
# 2001-06-23
# hack to fix error in export_curve introduced by Python 2.1
# now can't assign obj.color = array(...)
# Markus Gritsch (gritsch at iue.tuwien.ac.at)
# v.1.2 2001-11-23
# *) added 'xy_ratio' and 'custom_text' keywords to export()
# *) the pov-strings are now directly written to a file
# Scott David Daniels (Scott.Daniels at Acm.Org)
# v.1.3 2004-May-31 Move code to output rather than concatenate text.
"""This module exports a VPython scene as POV-Ray scene description code.
Lights and camera location from the current visual scene are included.
Optionally, you may specify a list of include files, and pov textures for
objects.
For an example of its use, see 'povexample.py'
convex objects are not exported.
"""
from visual import *
from string import rfind
__version__ = '0.1'
ihat=vector(1, 0, 0)
jhat=vector(0, 1, 0)
khat=vector(0, 0, 1)
def getpolar(a):
# a is a vector
# find rotation angles (standard polar coord)
xy = sqrt(a.x**2 + a.y**2)
theta = atan2(xy, a.z)
phi = atan2(a.y, a.x)
return theta, phi
def process_frame(a, dest):
# add in frame rotations & translations (may be nested)
frame_code = ''
fr = a.frame
while fr:
# orientation of frame.axis
theta, phi = getpolar(fr.axis)
aup = fr.up*1.0
# find rotation around x-axis (if fr.up <> jhat)
# "undo" theta & phi rotations so can find alpha
aup = rotate(aup, axis=khat, angle=-phi)
aup = rotate(aup, axis=jhat, angle=pi/2.0-theta)
a_sin = dot(cross(jhat, norm(aup)), ihat)
a_cos = dot(norm(aup), jhat)
alpha = atan2(a_sin, a_cos)
frx=alpha*180./pi
fry=-90+theta*180./pi
frz=phi*180./pi
print >>dest, ' rotate <%f, %f, %f>' % (frx, fry, frz)
print >>dest, ' translate <%f, %f, %f>' % (fr.x, fr.y, fr.z)
fr = fr.frame
def add_texture(a, dest):
# add in user-specified texture (will override color)
try:
print >>dest, ' texture { %s }' % a.pov_texture
except AttributeError:
pass
def export_sphere(a, dest):
print >>dest, """
sphere {
<%(posx)f, %(posy)f, %(posz)f>, %(radius)f
pigment { color rgb <%(red)f, %(green)f, %(blue)f> }
""" % { 'posx':a.x, 'posy':a.y, 'posz':a.z,
'radius':a.radius,
'red':a.red, 'green':a.green, 'blue':a.blue }
process_frame(a, dest)
add_texture(a, dest)
print >>dest, "}"
def export_box(a, dest):
# create box at origin along x-axis
# then rotate around x,y,z axes
# then translate to final location
# find rotations
theta, phi = getpolar(a.axis)
# find rotation around x-axis (if a.up <> jhat)
# "undo" theta & phi rotations so can find alpha
aup = a.up*1.0
aup = rotate(aup, axis=khat, angle=-phi)
aup = rotate(aup, axis=jhat, angle=pi/2.0-theta)
a_sin = dot(cross(jhat, norm(aup)), ihat)
a_cos = dot(norm(aup), jhat)
alpha = atan2(a_sin, a_cos)
# pos of visual box is at center
# generate two opposite corners for povray
pos1=vector(0, 0, 0) - a.size / 2.0
pos2=vector(0, 0, 0) + a.size / 2.0
print >>dest, """
box {
<%(posx)f, %(posy)f, %(posz)f>, <%(pos2x)f, %(pos2y)f, %(pos2z)f>
pigment {color rgb <%(red)f, %(green)f, %(blue)f>}
rotate <%(rotx)f, %(roty)f, %(rotz)f>
translate <%(transx)f, %(transy)f, %(transz)f>""" % {
'posx':pos1.x, 'posy':pos1.y, 'posz':pos1.z,
'pos2x':pos2.x, 'pos2y':pos2.y, 'pos2z':pos2.z,
'rotx':alpha*180./pi, 'roty':-90.+theta*180./pi, 'rotz':phi*180./pi,
'transx':a.x, 'transy':a.y, 'transz':a.z,
'red':a.red, 'green':a.green, 'blue':a.blue }
process_frame(a, dest)
add_texture(a, dest)
print >>dest, "}"
def export_cylinder(a, dest):
endpt1=a.pos
endpt2=a.pos+a.axis
print >>dest, """cylinder {
<%(posx)f, %(posy)f, %(posz)f>,<%(pos2x)f, %(pos2y)f, %(pos2z)f>, %(radius)f
pigment { color rgb <%(red)f, %(green)f, %(blue)f> }""" % {
'posx':a.x, 'posy':a.y, 'posz':a.z,
'pos2x':endpt2.x, 'pos2y':endpt2.y, 'pos2z':endpt2.z,
'red':a.red, 'green':a.green, 'blue':a.blue,
'radius':a.radius }
process_frame(a, dest)
add_texture(a, dest)
print >>dest, "}"
def export_curve(a, dest):
object_code = ''
if len(a.pos) > 1:
ii = 0
radius = max(0, a.radius)
ccyl = cylinder(pos=a.pos[0], axis=a.pos[0] - a.pos[0],
radius=radius or .1, color=tuple(a.color[0]),
frame=a.frame, visible=0)
csph = sphere(pos=a.pos[0], radius=ccyl.radius,
color=tuple(a.color[0]), frame=a.frame, visible=0)
if hasattr(a, 'pov_texture'):
csph.pov_texture = ccyl.pov_texture = a.pov_texture
for ii in xrange(len(a.pos) - 1):
# create a dummy cylinder to export
csph.pos = ccyl.pos = a.pos[ii]
ccyl.axis = a.pos[ii+1] - a.pos[ii]
csph.radius = ccyl.radius = radius or mag(ccyl.axis) / 200.
csph.color = ccyl.color = tuple(a.color[ii])
export_cylinder(ccyl, dest)
export_sphere(csph, dest)
ii = ii+1
ii = len(a.pos) - 1
csph.pos = a.pos[ii]
# csph.radius is identical
csph.color = tuple(a.color[ii])
export_sphere(csph, dest)
elif len(a.pos) == 1 and a.radius > 0:
csph = sphere(pos=a.pos[0], radius=a.radius,
color=tuple(a.color[0]), frame=a.frame, visible=0)
if hasattr(a, 'pov_texture'):
csph.pov_texture = a.pov_texture
export_sphere(csph, dest)
def export_ring(a, dest):
theta, phi = getpolar(a.axis)
print >>dest, """
torus {
%(radius)f, %(minorradius)f
pigment { color rgb <%(red)f, %(green)f, %(blue)f> }
rotate <0.0, 0.0, -90.0> // align with x-axis
rotate <%(rotx)f, %(roty)f, %(rotz)f>
translate <%(transx)f, %(transy)f, %(transz)f>""" % {
'radius':a.radius, 'minorradius':a.thickness,
'transx':a.x, 'transy':a.y, 'transz':a.z,
'rotx':0.0, 'roty':-90.+theta*180./pi, 'rotz':phi*180./pi,
'red':a.red, 'green':a.green, 'blue':a.blue }
process_frame(a, dest)
add_texture(a, dest)
print >>dest, "}"
def export_arrow(a, dest):
pyramid_template = """
object {Pyramid
pigment { color rgb <%(red)f, %(green)f, %(blue)f> }
scale <%(scalex)f, %(scaley)f, %(scalez)f>
rotate <0, 0, -90> // align with x-axis
rotate <%(rotx)f, %(roty)f, %(rotz)f>
translate <%(transx)f, %(transy)f, %(transz)f>
}
"""
al = a.length
hl = a.headlength
sl = al-hl # length of shaft
hw = a.headwidth
sw = a.shaftwidth
# head is a pyramid
ppos = a.pos+a.axis*(sl/al)
theta, phi = getpolar(a.axis)
print >>dest, """
object {Pyramid
pigment { color rgb <%(red)f, %(green)f, %(blue)f> }
scale <%(scalex)f, %(scaley)f, %(scalez)f>
rotate <0, 0, -90> // align with x-axis
rotate <%(rotx)f, %(roty)f, %(rotz)f>
translate <%(transx)f, %(transy)f, %(transz)f> """ % {
'scalex':hw, 'scaley':hl, 'scalez':hw,
'rotx':0., 'roty':-90.+theta*180./pi, 'rotz':phi*180./pi,
'red':a.red, 'green':a.green, 'blue':a.blue,
'transx':ppos.x, 'transy':ppos.y, 'transz':ppos.z }
process_frame(a, dest)
add_texture(a, dest)
print >>dest, "}"
# shaft is a box; need to create a dummy box
abox = box(pos=a.pos+a.axis*(sl/al)/2.0, axis=a.axis*(sl/al),
up = a.up, width=a.shaftwidth, height=a.shaftwidth,
color=a.color, frame=a.frame, visible = 0)
if hasattr(a, 'pov_texture'):
abox.pov_texture = a.pov_texture
export_box(abox, dest) ## ??? inhibit process_frame code. ???
def export_cone(a, dest):
pos2 = a.pos + a.axis
print >>dest, """
cone {
<%(posx)f, %(posy)f, %(posz)f>, %(radius)f
<%(pos2x)f, %(pos2y)f, %(pos2z)f>, %(minorradius)f
pigment { color rgb <%(red)f, %(green)f, %(blue)f> }""" % {
'radius':a.radius, 'minorradius':0.0,
'posx':a.x, 'posy':a.y, 'posz':a.z,
'pos2x':pos2.x, 'pos2y':pos2.y, 'pos2z':pos2.z,
'red':a.red, 'green':a.green, 'blue':a.blue }
process_frame(a, dest)
add_texture(a, dest)
print >>dest, "}"
def dontexport(a, dest):
pass
export_table = dict(
sphere=export_sphere,
box=export_box,
cylinder=export_cylinder,
curve=export_curve,
ring=export_ring,
arrow=export_arrow,
cone=export_cone,
frame=dontexport)
def export(display=None, filename=None, include_list=None,
xy_ratio=4./3., custom_text='', shadowless=False):
if display is None: # no display specified so find active display
dummy = sphere(visible=0)
display = dummy.display
del dummy
if filename is None:
filename = display.title + '.pov'
dest = open(filename, 'wt')
print >>dest, '// povray code generated by povexport.py v%s' % __version__
if include_list is not None:
for x in include_list:
print >>dest, '#include "%s"\n' % x
print >>dest, custom_text
# The next is needed only for arrows.
print >>dest, """// Four-sided pyramid from shapes2.inc, slightly modified.
#declare Pyramid = union {
triangle { <-1, 0, -1>, <+1, 0, -1>, <0, 1, 0> }
triangle { <+1, 0, -1>, <+1, 0, +1>, <0, 1, 0> }
triangle { <-1, 0, +1>, <+1, 0, +1>, <0, 1, 0> }
triangle { <-1, 0, +1>, <-1, 0, -1>, <0, 1, 0> }
triangle { <-1, 0, -1>, <-1, 0, +1>, <1, 0, 1> }
triangle { <-1, 0, -1>, <+1, 0, -1>, <1, 0, 1> }
scale <.5, 1, .5> // so height = width = 1
}"""
print >>dest, 'global_settings { ambient_light rgb',
print >>dest, '<%(a)f, %(a)f, %(a)f> }' % {'a': display.ambient * 10}
print >>dest, 'background { color rgb <%(r)f, %(g)f, %(b)f> }' % {
'r':display.background[0], 'g':display.background[1],
'b':display.background[2] }
# begin povray setup
for i in arange(len(display.lights)): # reproduce visual lighting (not ideal, but good approximation)
light = display.lights[i] # vector in direction of light
intensity = mag(light) # intensity of light (all lights are white)
pos = norm(light) * max(display.range) * 100.0 # far away to simulate parallel light
print >>dest, """light_source { <%(posx)f, %(posy)f, %(posz)f>
color rgb <%(int)f, %(int)f, %(int)f>""" % {
'posx':pos.x, 'posy':pos.y, 'posz':pos.z, 'int':intensity*5/3.}
if shadowless:
print >>dest, ' shadowless'
print >>dest, '}'
## ??? Frame code for lights???
cpos = display.mouse.camera
ctr = display.center
cup = display.up
print >>dest, """camera {
right <-%(xyratio)f, 0, 0> //visual uses right-handed coord. system
location <%(posx)f, %(posy)f, %(posz)f>
sky <%(upx)f, %(upy)f, %(upz)f>
look_at <%(pos2x)f, %(pos2y)f, %(pos2z)f>
angle %(fov)f
rotate <0, 0, 0>
}""" % { 'xyratio':xy_ratio,
'posx':cpos.x, 'posy':cpos.y, 'posz':cpos.z,
'upx':cup.x, 'upy':cup.y, 'upz':cup.z,
'pos2x':ctr.x, 'pos2y':ctr.y, 'pos2z':ctr.z,
'fov':display.fov*180/pi }
exports = export_table.copy()
for obj in display.objects:
name = str(obj.__class__)
try:
function = exports[name]
except AttributeError:
print 'WARNING: export function for %s not implemented' % name
exports[name] = dontexport # Only report each type once.
else:
function(obj, dest)
dest.close()
return 'The export() function no longer returns the scene as an\n' \
'endless POV-Ray string, but saves it to a file instead.'
if __name__ == '__main__':
import sys
print 'povexport %s\nPython %s' % (__version__, sys.version)
print __doc__
More information about the Edu-sig
mailing list