
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