[Edu-sig] Hypertoons!

Kirby Urner urnerk at qwest.net
Fri Mar 11 18:43:25 CET 2005


I've made some headway implementing a primitive hypertoon using VPython.

I came up with the hypertoon concept some years back, per this 1996 web
page: http://www.grunch.net/synergetics/hypertoon.html

The basic idea is you have this set of key frames, say A-F, and a set of
cartoons or "scenarios" which always start and finish with one of the key
frames.  In between you have some transformation that takes you smoothly
from the starting key frame to the ending one.

A dictionary of scenarios might look like this:

scenarios = {}

# Scenario( function, starting frame, ending frame)
scenarios['sc0'] = Scenario(trans0, 'A', 'B')
scenarios['sc1'] = Scenario(trans1, 'A', 'C')
scenarios['sc2'] = Scenario(trans2, 'A', 'D')
scenarios['sc3'] = Scenario(trans3, 'B', 'C')
scenarios['sc4'] = Scenario(trans4, 'C', 'D')
scenarios['sc5'] = Scenario(trans5, 'D', 'B')
...

So you get this spaghetti ball, or network, of interconnected nodes (the key
frames), all interconnected by transformations (the scenarios).  At each
node, you may randomize the selection process, as to where to go next.  

    def play(self, n):
        node = keyframes[0]
        lastthing = None
        for i in xrange(n): 
            nodelist   = self.nodes[node] # list of candidate toons
            nextsc     = nodelist[randint(0,len(nodelist)-1)] # random pick
            lastthing  = self.scenarios[nextsc].play(lastthing) # play it
            node = self.scenarios[nextsc].finish # here's where you end up

The result is a continuous smooth motion visualization, with a lot of
repetition, and with a lot of random sequencing.  If the network is quite
large, then you might keep seeing new stuff even after quite some time.

Per my interest in geometry, my theme for my first VPython hypertoon is
polyhedra and their interrelationships per what Bucky Fuller called the
concentric hierarchy (or cosmic hierarchy when he was being cosmic).  I
started out with the following key frames:

    # A: rh dodeca all alone
    # B: octahedron all alone
    # C: cube all alone
    # D: tetra all alone

Later I added:

    # E: cubocta all alone
    # F: Icosa all alone  

Not sure what's with this "all alone" -- emphasizing that we end up with
nothing else on stage I guess.

The first thing I did was script scenarios among the initial four key
frames.  I wrote a transformation in each direction, e.g. if there's a toon
from A to B, there's also one from B to A.

By transformation I don't mean anything very fancy.  I basically just trace
out edges (they progressively elongate, so each scenario takes a few seconds
-- I adjust the frame rate).  For example:

def trans10(thing):
    print "trans10: tetra -> cube"                
    tet = rbf.Tetra()
    # D: tetra all alone
    tet.draw()
    if thing is not None:
        thing.delete()            
    invtet = rbf.Invtetra()
    invtet.draw(trace=True)
    cb = rbf.Cube()
    cb.draw(trace=True)
    tet.delete()
    invtet.delete()
    # C: cube all alone
    return cb

Here we're going from the tetrahedron key frame to the cube key frame.  I do
this by drawing an inverted tetrahedron (black), intersecting the first
one's mid edges, then connecting the resulting 8 points by tracing out a
(green) cube.  Then I delete the two tetrahedral, leaving only the cube.

There's one subtlety here:  each scenario returns the final polyhedron as an
object, and this gets passed in to the next scenario as an argument.  The
next scenario then superimposes an identical shape, before deleting the one
that got passed in.  The purpose is to create overlap, so there's no point
at which the stage is entirely empty.

The fanciest I've gotten with the transformations so far is between E and F
(the icosahedron and the cuboctahedron).  For those of you familiar with
Bucky's geometry (actually a philosophy), there's this thing called the
jitterbug transformation that relates the two.  I found this somewhat
difficult to implement in VPython and ended up with a kind of
double-buffering, where two polyhedra progressively draw and delete
themselves as the vertices shift position.

I'm posting the source code to my web site, in case you want to eyeball it
in any detail (or even run it).  I'll be changing it of course, as time
permits.  This version requires Python 2.4, meaning I'm using the VPython
2.4 experimental.  However, it'd take only minor tweaks to make it run in
2.3 (I treat 'set' as a built-in and use 'sorted' is all).

hypertoons.py --  the main module wherein all the scenarios are defined and
from whence the hypertoon is launched

rbf.py -- a library of concentric hierarchy shapes, repurposed from POV-Ray
work to support VPython vectors instead.  The repurposing is incomplete and
fuzzy around the edges.

coords.py -- where I've rolled my own vector classes, except now I hardly
need 'em cuz I'm using VPython's native class.  

See:  http://www.4dsolutions.net/ocn/python/hypertoons/

The only reason coords.py is still in the picture is I get the whole show
rolling by defining a large set of vertices as globals within rbf.py.  These
are the key "points of interest" within the concentric hierarchy, out of
which all the polyhedra are built.  

For historical reasons, these key points start out being defined in this
weird coordinate system defined by four rays emanating from the center of a
tetrahedron to its four corners, labeled (1,0,0,0), (0,1,0,0), (0,0,1,0),
(0,0,0,1) -- a coordinate system that requires no negative numbers.  It so
happens that our points of interest are very easily expressed as vector sums
(linear combinations) of these four.

I immediately convert these weirdo vectors to traditional XYZ vectors in
rbf.py, but I'm still using the old coords.py to start with with "Qvectors"
(Q for Quadrays)[1].

Here's what that looks like in more detail (defining A-Z):

ORIGIN = vector(0,0,0)

A = vector(Qvector((1,0,0,0)).xyz)  # center to corner of tetrahedron
B = vector(Qvector((0,1,0,0)).xyz)  #          "
C = vector(Qvector((0,0,1,0)).xyz)  #          "
D = vector(Qvector((0,0,0,1)).xyz)  #          "

# tetrahedron's dual (also a tetrahedron i.e. inverted tet)
E,F,G,H = B+C+D,A+C+D,A+B+D,A+B+C   

# tetrahedron + dual (inverted tet) = duo-tet cube

# octahedron vertices from pairs of tetrahedron radials
I,J,K,L,M,N = A+B, A+C, A+D, B+C, B+D, C+D

# octahedron + dual (cube) = rhombic dodecahedron

# cuboctahedron vertices from pairs of octahedron radials
O,P,Q,R,S,T = I+J, I+K, I+L, I+M, N+J, N+K
U,V,W,X,Y,Z = N+L, N+M, J+L, L+M, M+K, K+J      

Kirby

PS: Something I've discovered about VPython:  apparently copy.deepcopy
doesn't work reliably to copy vector objects.

[1] http://www.grunch.net/synergetics/quadintro.html




More information about the Edu-sig mailing list