""" Version 3.1 April 4, 2005 This version is compatible with Python 2.4 and above ONLY. Creates a network of VPython animations connected through key frames, with a random selection at each frame. Like a network of subway tunnels, and a choice of where to go next at each station (tunnel = scenario, nodelist = options of where to go next i.e. any scenario starting with current key frame. The theme of this hypertoon is Buckminster Fuller's concentric hierarchy of polyhedra, complete with jitterbug transformation and allusions to the CCP. Depends on / compatible with rbf.py Version 1.3 (March 14, 2005) =============================================================== History April 4, 2005: in an effort to prove to myself that I could still think straight, after a week-long fever, I added a couple more scenarios (one to shrink-wrap the rhombic triacontahedron around a sphere, one to draw a smaller cuboctahedron inside the green cube (volume 2.5)). March 31, 2005: Scott's modifications were to an older version, so integrated the changes. March 27, 2005: by Scott David Daniels to make use decorators syntax (2.4). The transition function returns the original (a transition), but also populates the transitions dictionary, thereby eliminating much code. He also beefed up the command line situation (and used random.choice instead of random.randint -- now why didn't I think of that?). March 15, 2005: got rid of silly eval and Vset class -- a plain dictionary works incorporated changes by John Zelle (more startup options, 2.3 compat) rewrote all the jitterbug and cube transformations to redraw existing cylinders and vertices, instead of using delete/draw double buffering -- makes for much smoother motions March 14 2005: 29 scenarios, 8 key frames Fixed bugs in jitterbug Put rbf canonical points inside a class, use copies of Sped it up, other aesthetic improvements Added more five-fold shapes place balls growing/shrinking Removed dependencies on colors.py coords.py Implemented threading (2 hypertoons run together) Added stereo option First posted: March 11 2005 16 scenarios, 6 key frames Original proof of concept Background posted to Python/edu-sig http://mail.python.org/pipermail/edu-sig/2005-March/004499.html John Zelle gets it in stereo! http://mail.python.org/pipermail/edu-sig/2005-March/004503.html Kirby Urner 4D Solutions """ from random import choice import threading import rbf import sys transitions = {} verbose = False def transition(initial, final): def record_transition(transition): try: surprise = transitions[initial][final] except KeyError: pass # this is expected else: print >>sys.stderr, 'Changing %s -> %s: from %r to %r' % ( initial, final, surprise.__name__, transition.__name__) transitions.setdefault(initial, {})[final] = transition return transition return record_transition @transition('rh', 'octa') def trans0(thing): """trans0: rh -> octa""" rh = rbf.Rhdodeca() # A: rh dodeca all alone rh.draw() if thing is not None: thing.delete() oc = rbf.Octa() oc.draw(trace=True) rh.delete() # B: octahedron all alone return oc @transition('rh', 'cube') def trans1(thing): """trans1: rh -> cube""" rh = rbf.Rhdodeca() # A: rh dodeca all alone rh.draw() if thing is not None: thing.delete() cb = rbf.Cube() cb.draw(trace=True) rh.delete() # C: cube all alone return cb @transition('rh', 'tetra') def trans2(thing): """trans2: rh -> tetra""" rh = rbf.Rhdodeca() # A: rh dodeca all alone rh.draw() if thing is not None: thing.delete() cb = rbf.Cube() cb.draw(trace=True) tet = rbf.Tetra() tet.draw(trace=True) rh.delete() cb.delete() # D: tetra all alone return tet @transition('octa', 'cube') def trans3(thing): """trans3: octa -> cube""" oc = rbf.Octa() # B: octahedron all alone oc.draw() if thing is not None: thing.delete() cb = rbf.Cube() cb.draw(trace=True) oc.delete() # C: cube all alone return cb @transition('cube', 'tetra') def trans4(thing): """trans4: cube -> tetra""" cb = rbf.Cube() # C: cube all alone cb.draw() if thing is not None: thing.delete() tet = rbf.Tetra() tet.draw(trace=True) cb.delete() # D: tetra all alone return tet @transition('tetra', 'octa') def trans5(thing): """trans5: tetra -> octa""" tet = rbf.Tetra() # D: tetra all alone tet.draw() if thing is not None: thing.delete() oc = rbf.Octa() oc.draw(trace=True) tet.delete() # B: octahedron all alone return oc @transition('octa', 'rh') def trans6(thing): """trans6: octa -> rh dodeca""" oc = rbf.Octa() # B: octahedron all alone oc.draw() if thing is not None: thing.delete() rh = rbf.Rhdodeca() rh.draw(trace=True) oc.delete() # A: rh dodeca all alone return rh @transition('cube', 'rh') def trans7(thing): """trans7: cube -> rh dodeca""" cu = rbf.Cube() # C: cube all alone cu.draw() if thing is not None: thing.delete() rh = rbf.Rhdodeca() rh.draw(trace=True) cu.delete() # A: rh dodeca all alone return rh @transition('tetra', 'rh') def trans8(thing): """trans8: tetra -> rh dodeca""" tet = rbf.Tetra() # D: tetra all alone tet.draw() if thing is not None: thing.delete() cb = rbf.Cube() cb.draw(trace=True) rh = rbf.Rhdodeca() rh.draw(trace=True) tet.delete() cb.delete() # A: rh dodeca all alone return rh @transition('cube', 'octa') def trans9(thing): """trans9: cube -> octa""" cb = rbf.Cube() # C: cube all alone cb.draw() if thing is not None: thing.delete() oc = rbf.Octa() oc.draw(trace=True) cb.delete() # B: octahedron all alone return oc @transition('tetra', 'cube') def trans10(thing): """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 @transition('octa', 'tetra') def trans11(thing): """trans11: octa -> tetra""" oc = rbf.Octa() # B: octahedron all alone oc.draw() if thing is not None: thing.delete() cb = rbf.Cube() cb.draw(trace=True) tet = rbf.Tetra() tet.draw(trace=True) cb.delete() oc.delete() # D: tetra all alone return tet @transition('rh', 'cubocta') def trans12(thing): """trans12: rh -> cubocta""" rh = rbf.Rhdodeca() # A: rh dodeca all alone rh.draw() if thing is not None: thing.delete() sp = rbf.VEspokes() sp.draw(trace=True) cu = rbf.Cubocta() cu.draw(trace=True) sp.delete() rh.delete() # E: cubocta all alone return cu @transition('cubocta', 'rh') def trans13(thing): """trans13: cubocta -> rh""" cu = rbf.Cubocta() # E: Cubocta all alone cu.draw() if thing is not None: thing.delete() sp = rbf.VEspokes() sp.draw(trace=True) rh = rbf.Rhdodeca() rh.draw(trace=True) sp.delete() cu.delete() # A: rh dodeca all alone return rh @transition('cubocta', 'icosa') def trans14(thing): """trans14: cubocta -> icosa""" cu = rbf.Cubocta() # E: Cubocta all alone cu.draw() if thing is not None: thing.delete() ic = rbf.Icosa() tracks = [] for let in ['O','P','Q','R','S','T','U','V','W','X','Y','Z']: tracks.append([ cu.vertices[let], ic.vertices[let+'1'] - cu.vertices[let] ]) for i in range(30): for v in tracks: v[0] += 1/30.0 * v[1] rbf.rate(500) cu.redraw() ic.draw() cu.delete() # F: Icosa all alone return ic @transition('icosa', 'cubocta') def trans15(thing): """trans15: icosa -> cubocta""" ic = rbf.Icosa() # F: Icosa all alone ic.draw() if thing is not None: thing.delete() cu = rbf.Cubocta() tracks = [] for let in ['O','P','Q','R','S','T','U','V','W','X','Y','Z']: tracks.append( [ ic.vertices[let+'1'], cu.vertices[let] - ic.vertices[let+'1'] ]) for i in range(30): for v in tracks: v[0] += 1/30.0 * v[1] rbf.rate(500) ic.redraw() cu.draw() ic.delete() return cu @transition('cubocta', 'cubocta') def trans16(thing): """trans16: cubocta -> cubocta""" cu = rbf.Cubocta() # E: Cubocta all alone cu.draw() if thing is not None: thing.delete() vs = cu.vertices.values() vs.append(rbf.vector(0,0,0)) wow(vs, 500) return cu @transition('icosa', 'icosa') def trans17(thing): """trans17: icosa -> icosa""" ic = rbf.Icosa() # E: Icosa all alone ic.draw() if thing is not None: thing.delete() vs = ic.vertices.values() wow(vs, 500) return ic @transition('rh', 'rh') def trans18(thing): """trans18: rh -> rh""" rh = rbf.Rhdodeca() # A: rh dodeca all alone rh.draw() if thing is not None: thing.delete() vs = [] vs.append(rbf.vector(0,0,0)) wow(vs, 100) return rh @transition('icosa', 'octa') def trans19(thing): """trans19: icosa -> octa""" ic = rbf.Jiticosa() # F: Icosa all alone ic.draw() if thing is not None: thing.delete() oc = rbf.Octa() tracks = [] for iclet, oclet in zip(['O','P','Q','R','S','T','U','V','W','X','Y','Z'], ['I','K','L','I','N','K','L','N','J','M','M','J']): tracks.append( [ ic.vertices[iclet+'1'], oc.vertices[oclet] - ic.vertices[iclet+'1'] ]) for i in range(30): for v in tracks: v[0] += 1/30.0 * v[1] rbf.rate(500) ic.redraw() oc.draw() ic.delete() # E: Octa all alone return oc @transition('octa', 'icosa') def trans20(thing): """trans20: octa -> icosa""" ic = rbf.Jiticosa() oc = rbf.Octa() for iclet, oclet in zip(['O1','P1','Q1','R1','S1','T1','U1','V1','W1','X1','Y1','Z1'], ['I','K','L','I','N','K','L','N','J','M','M','J']): v = oc.vertices[oclet] ic.vertices[iclet] = rbf.vector(v.x, v.y, v.z) # B: Octa all alone ic.draw() if thing is not None: thing.delete() ic3 = rbf.Icosa() tracks = [] for iclet in ['O1','P1','Q1','R1','S1','T1','U1','V1','W1','X1','Y1','Z1']: tracks.append( [ ic.vertices[iclet], ic3.vertices[iclet] - ic.vertices[iclet] ]) for i in range(30): for v in tracks: v[0] += 1/30.0 * v[1] rbf.rate(500) ic.redraw() ic3.draw() ic.delete() # rbf.init() # F: Icosa all alone return ic3 @transition('icosa', 'pentdodeca') def trans21(thing): """trans21: icosa -> pentdodeca""" ic = rbf.Icosa() # F: Icosa all alone ic.draw() if thing is not None: thing.delete() pd = rbf.Pentdodeca() pd.draw(trace=True) ic.delete() # G: Pdodeca all alone return pd @transition('pentdodeca', 'icosa') def trans22(thing): """trans22: pentdodeca -> icosa""" pd = rbf.Pentdodeca() # G: Pdodeca all alone pd.draw() if thing is not None: thing.delete() ic = rbf.Icosa() ic.draw(trace=True) pd.delete() # F: Icosa all alone return ic @transition('pentdodeca', 'cube') def trans23(thing): """trans23: pentdodeca -> cube""" pd = rbf.Pentdodeca() # G: Pdodeca pd.draw() if thing is not None: thing.delete() cu1 = rbf.Pentcube() cu1.draw(trace=True) cu = rbf.Cube() tracks = [] for let1, let2 in zip(['B','G','D','E','C','A','F','H'], ['j','b','p','q','f','l','s','m']): tracks.append( [ cu1.vertices[let2], cu.vertices[let1] - cu1.vertices[let2] ]) for i in range(30): for v in tracks: v[0] += 1/30.0 * v[1] rbf.rate(500) cu1.redraw() cu.draw() cu1.delete(1000) pd.delete() # C: Cube all alone return cu @transition('cube', 'pentdodeca') def trans24(thing): """trans24: cube -> pentdodeca""" cu1 = rbf.Cube() cu1.draw() # C: cube all alone if thing is not None: thing.delete() cu = rbf.Pentcube() tracks = [] for let1, let2 in zip(['B','G','D','E','C','A','F','H'], ['j','b','p','q','f','l','s','m']): tracks.append( [ cu1.vertices[let1], cu.vertices[let2] - cu1.vertices[let1] ]) for i in range(30): for v in tracks: v[0] += 1/30.0 * v[1] rbf.rate(500) cu1.redraw() cu.draw() cu1.delete(1000) pd = rbf.Pentdodeca() pd.draw(trace=True) cu.delete() # G: Pdodeca all alone return pd @transition('pentdodeca', 'rh_triaconta') def trans25(thing): """trans25: pentdodeca -> rh triaconta""" pd = rbf.Pentdodeca() # G: Pdodeca all alone pd.draw() if thing is not None: thing.delete() ic = rbf.Icosa() ic.draw(trace=True) rt = rbf.Rhtriaconta() rt.draw(trace=True) pd.delete() ic.delete() # H: Rhtriac all alone return rt @transition('rh_triaconta', 'pentdodeca') def trans26(thing): """trans26: rh triaconta -> Pentdodeca""" rt = rbf.Rhtriaconta() # H: Rtriac all alone rt.draw() if thing is not None: thing.delete() pd = rbf.Pentdodeca() pd.draw(trace=True) rt.delete() # G: Pentdodeca all alone return pd @transition('rh_triaconta', 'icosa') def trans27(thing): """trans27: rh triaconta -> Icosa""" rt = rbf.Rhtriaconta() # H: Rtriac all alone rt.draw() if thing is not None: thing.delete() ic = rbf.Icosa() ic.draw(trace=True) rt.delete() # F: Icosa all alone return ic @transition('icosa', 'rh_triaconta') def trans28(thing): """trans28: Icosa -> rh triaconta""" ic = rbf.Icosa() # F: Icosa all alone ic.draw() if thing is not None: thing.delete() rt = rbf.Rhtriaconta() rt.draw(trace=True) ic.delete() # H: Rhtriac all alone return rt @transition('cube', 'cubocta') def trans29(thing): """trans29: Cube -> Cubocta""" cu = rbf.Cube() cu.draw() if thing is not None: thing.delete() ve = rbf.Cubocta() smve = ve * 0.5 smve.draw(True) tracks = [] for let1, let2 in zip(ve.vertices.keys(), ve.vertices.keys()): tracks.append( [ smve.vertices[let1], ve.vertices[let2] - smve.vertices[let1] ]) for i in range(30): for v in tracks: v[0] += 1/30.0 * v[1] rbf.rate(500) smve.redraw() ve.draw() smve.delete(1000) cu.delete(1000) # E: Cubocta all alone return ve @transition('rh_triaconta', 'rh') def trans30(thing): """trans30: rh triaconta -> rh""" rt = rbf.Rhtriaconta() # H: Rtriac all alone rt.draw() if thing is not None: thing.delete() tracks = [] smrt = rt * rbf.Vset['ef'] for let1, let2 in zip(rt.vertices.keys(), rt.vertices.keys()): tracks.append( [ rt.vertices[let1], smrt.vertices[let2] - rt.vertices[let1] ]) for i in range(30): for v in tracks: v[0] += 1/30.0 * v[1] rbf.rate(500) rt.redraw() smrt.draw() rt.delete(1000) rh = rbf.Rhdodeca() vs = [] vs.append(rbf.vector(0,0,0)) wow(vs, 100) rh.draw(trace=True) smrt.delete(1000) wow(vs, 100) return rh def wow(vs, r=60): sphs = [] maxlen = rbf.mag(rbf.Vset['A'] - rbf.Vset['B'])/2.0 for v in vs: sphs.append(rbf.sphere(pos=v, radius=0, color = (1,0.7,0.2))) for i in range(1,101): for s in sphs: rbf.rate(r) s.radius = maxlen * i/100 for i in range(100,-1,-1): for s in sphs: rbf.rate(r) s.radius = maxlen * i/100 def hypertoon(repetitions, node=None): """Start an N-transition hypertoon. node may be a name like 'cube'""" liveobject = None if node is None: node = choice(transitions.keys()) for x in range(repetitions): possibilities = transitions[node] coming, function = choice(possibilities.items()) if verbose: print node, coming, function.__name__, function.__doc__ liveobject = function(liveobject) node = coming def setup(stereo=False, fullscreen=True): scene2 = rbf.display(title='Hypertoon!', center=(0,0,0), background=(.5, .5, .5)) scene2.ambient = .5 scene2.fullscreen = fullscreen if stereo: if stereo is True: stereo = 'redblue' scene2.stereo = stereo scene2.stereodepth = 1.5 xyz = rbf.XYZaxes() xyz = 1.5 * xyz xyz.draw() scene2.autoscale = 0 xyz.delete() return scene2 class Mythread(threading.Thread): def __init__(self, hypertoon, number, shape=None): super(Mythread, self).__init__(None) self.hypertoon = hypertoon self.n = number self.shape = shape def run(self): self.hypertoon(self.n, self.shape) def main(number, stereo=False, threads=True, fullscreen=True, shape=None): if verbose: for base, choices in sorted(transitions.iteritems()): print '%s: %s' % (base, ', '.join(sorted(choices))) if shape is None: shape = choice(transitions.keys()) scene = setup(stereo, fullscreen) if threads: t1 = Mythread(hypertoon, number, shape) t2 = Mythread(hypertoon, number, shape) t1.start() t2.start() t1.join() t2.join() print "Threads complete" else: hypertoon(number, shape) if __name__ == '__main__': _stereo = _threads = verbose = _windowed = False _shape = None _repetitions = 20 for name in sys.argv[1:]: if '-H' == name: print '-R# -S: stereo, -T: threads -W: windowed, -V: verbose', print '-Spassive or -Sredblue' print 'or a starting shape:', ' '.join(sorted(transitions)) raise SystemExit if '-S' == name: _stereo = True elif '-T' == name: _threads = True elif '-V' == name: verbose = True # A global elif '-W' == name: _windowed = True elif name.startswith('-R'): _repetitions = int(name[2:]) elif name.startswith('-S'): _stereo = name[2:] elif name in transitions: _shape = name else: print 'Ignoring parameter %r' % name main(_repetitions, _stereo, _threads, not _windowed, _shape)