"""
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
March 15, 2009: added scenario 31, squished some bugs tracing to
the fact that scalar multiplication of a polyhedron alters the
original in this implementation of rbf.py i.e. doesn't return
a new copy. Since the last modifications, we've added a volume
7.5 rhombic triacontahedron or radius phi/sqrt(2) that intersects
the volume 6 rhombic dodecahedron. We develop it starting with
the rt of volume 5, scaling by 3rd root of 1.5 i.e. volume goes
up as a 3rd power to 7.5.
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 = True
def transition(initial, final):
def record_transition(transition):
try:
surprise = transitions[initial][final]
except KeyError:
pass
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()
rh.draw()
if thing is not None:
thing.delete()
oc = rbf.Octa()
oc.draw(trace=True)
rh.delete()
return oc
@transition('rh', 'cube')
def trans1(thing):
"""trans1: rh -> cube"""
rh = rbf.Rhdodeca()
rh.draw()
if thing is not None:
thing.delete()
cb = rbf.Cube()
cb.draw(trace=True)
rh.delete()
return cb
@transition('rh', 'tetra')
def trans2(thing):
"""trans2: rh -> tetra"""
rh = rbf.Rhdodeca()
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()
return tet
@transition('octa', 'cube')
def trans3(thing):
"""trans3: octa -> cube"""
oc = rbf.Octa()
oc.draw()
if thing is not None:
thing.delete()
cb = rbf.Cube()
cb.draw(trace=True)
oc.delete()
return cb
@transition('cube', 'tetra')
def trans4(thing):
"""trans4: cube -> tetra"""
cb = rbf.Cube()
cb.draw()
if thing is not None:
thing.delete()
tet = rbf.Tetra()
tet.draw(trace=True)
cb.delete()
return tet
@transition('tetra', 'octa')
def trans5(thing):
"""trans5: tetra -> octa"""
tet = rbf.Tetra()
tet.draw()
if thing is not None:
thing.delete()
oc = rbf.Octa()
oc.draw(trace=True)
tet.delete()
return oc
@transition('octa', 'rh')
def trans6(thing):
"""trans6: octa -> rh dodeca"""
oc = rbf.Octa()
oc.draw()
if thing is not None:
thing.delete()
rh = rbf.Rhdodeca()
rh.draw(trace=True)
oc.delete()
return rh
@transition('cube', 'rh')
def trans7(thing):
"""trans7: cube -> rh dodeca"""
cb = rbf.Cube()
cb.draw()
if thing is not None:
thing.delete()
rh = rbf.Rhdodeca()
rh.draw(trace=True)
cb.delete()
return rh
@transition('tetra', 'rh')
def trans8(thing):
"""trans8: tetra -> rh dodeca"""
tet = rbf.Tetra()
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()
return rh
@transition('cube', 'octa')
def trans9(thing):
"""trans9: cube -> octa"""
cb = rbf.Cube()
cb.draw()
if thing is not None:
thing.delete()
oc = rbf.Octa()
oc.draw(trace=True)
cb.delete()
return oc
@transition('tetra', 'cube')
def trans10(thing):
"""trans10: tetra -> cube"""
tet = rbf.Tetra()
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()
return cb
@transition('octa', 'tetra')
def trans11(thing):
"""trans11: octa -> tetra"""
oc = rbf.Octa()
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()
return tet
@transition('rh', 'cubocta')
def trans12(thing):
"""trans12: rh -> cubocta"""
rh = rbf.Rhdodeca()
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()
return cu
@transition('cubocta', 'rh')
def trans13(thing):
"""trans13: cubocta -> rh"""
cu = rbf.Cubocta()
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()
return rh
@transition('cubocta', 'icosa')
def trans14(thing):
"""trans14: cubocta -> icosa"""
cu = rbf.Cubocta()
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()
return ic
@transition('icosa', 'cubocta')
def trans15(thing):
"""trans15: icosa -> cubocta"""
ic = rbf.Icosa()
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()
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()
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()
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()
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()
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)
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()
return ic3
@transition('icosa', 'pentdodeca')
def trans21(thing):
"""trans21: icosa -> pentdodeca"""
ic = rbf.Icosa()
ic.draw()
if thing is not None:
thing.delete()
pd = rbf.Pentdodeca()
pd.draw(trace=True)
ic.delete()
return pd
@transition('pentdodeca', 'icosa')
def trans22(thing):
"""trans22: pentdodeca -> icosa"""
pd = rbf.Pentdodeca()
pd.draw()
if thing is not None:
thing.delete()
ic = rbf.Icosa()
ic.draw(trace=True)
pd.delete()
return ic
@transition('pentdodeca', 'cube')
def trans23(thing):
"""trans23: pentdodeca -> cube"""
pd = rbf.Pentdodeca()
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()
return cu
@transition('cube', 'pentdodeca')
def trans24(thing):
"""trans24: cube -> pentdodeca"""
cu1 = rbf.Cube()
cu1.draw()
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()
return pd
@transition('pentdodeca', 'rh_triaconta')
def trans25(thing):
"""trans25: pentdodeca -> rh triaconta"""
pd = rbf.Pentdodeca()
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()
return rt
@transition('rh_triaconta', 'pentdodeca')
def trans26(thing):
"""trans26: rh triaconta -> Pentdodeca"""
rt = rbf.Rhtriaconta()
rt.draw()
if thing is not None:
thing.delete()
pd = rbf.Pentdodeca()
pd.draw(trace=True)
rt.delete()
return pd
@transition('rh_triaconta', 'icosa')
def trans27(thing):
"""trans27: rh triaconta -> Icosa"""
rt = rbf.Rhtriaconta()
rt.draw()
if thing is not None:
thing.delete()
ic = rbf.Icosa()
ic.draw(trace=True)
rt.delete()
return ic
@transition('icosa', 'rh_triaconta')
def trans28(thing):
"""trans28: Icosa -> rh triaconta"""
ic = rbf.Icosa()
ic.draw()
if thing is not None:
thing.delete()
rt = rbf.Rhtriaconta()
rt.draw(trace=True)
ic.delete()
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 = rbf.Cubocta() * 0.5
smve.draw(True)
tracks = []
for let1, let2 in zip(smve.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)
return ve
@transition('rh_triaconta', 'rh')
def trans30(thing):
"""trans30: rh triaconta -> rh"""
rt = rbf.Rhtriaconta()
rt.draw()
if thing is not None:
thing.delete()
tracks = []
smrt = rbf.Rhtriaconta() * rbf.Vset['ef']
for let1, let2 in zip(rt.vertices.keys(), smrt.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))
rh.draw(trace=True)
smrt.delete(1000)
return rh
@transition('rh', 'rh_triaconta')
def trans31(thing):
"trans31: rh -> rh triaconta (7.5) -> rh triaconta (dual)"
rh = rbf.Rhdodeca()
rh.draw()
if thing is not None:
thing.delete()
rt = rbf.Rhtriaconta()
bgrt = rbf.Rhtriaconta() * rbf.Vset['ef'] * rbf.Vset['tf']
bgrt = bgrt * pow(1.5, 1./3)
bgrt.draw(trace=True)
tracks = []
for let1, let2 in zip(bgrt.vertices.keys(), rt.vertices.keys()):
tracks.append( [ bgrt.vertices[let1],
rt.vertices[let2] - bgrt.vertices[let1] ])
for i in range(30):
for v in tracks:
v[0] += 1/30.0 * v[1]
rbf.rate(500)
bgrt.redraw()
rt.draw(trace=True)
bgrt.delete(1000)
rh.delete(1000)
return rt
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
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)
# code highlighted using py2html.py version 0.8