[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