
ajsiegel@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. # ruth chabay, carnegie mellon university (rchabay@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@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@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@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__