bgneal@46
|
1 """ Code example from Complexity and Computation, a book about
|
bgneal@46
|
2 exploring complexity science with Python. Available free from
|
bgneal@46
|
3
|
bgneal@46
|
4 http://greenteapress.com/complexity
|
bgneal@46
|
5
|
bgneal@46
|
6 Copyright 2011 Allen B. Downey.
|
bgneal@46
|
7 Distributed under the GNU General Public License at gnu.org/licenses/gpl.html.
|
bgneal@46
|
8 """
|
bgneal@46
|
9
|
bgneal@46
|
10 import numpy
|
bgneal@46
|
11
|
bgneal@46
|
12
|
bgneal@46
|
13 class UnimplementedMethodException(Exception):
|
bgneal@46
|
14 """Used to indicate that a child class has not implemented an
|
bgneal@46
|
15 abstract method."""
|
bgneal@46
|
16
|
bgneal@46
|
17
|
bgneal@46
|
18 class Drawer(object):
|
bgneal@46
|
19 """Drawer is an abstract class that should not be instantiated.
|
bgneal@46
|
20 It defines the interface for a CA drawer; child classes of Drawer
|
bgneal@46
|
21 should implement draw, show and save.
|
bgneal@46
|
22
|
bgneal@46
|
23 If draw_array is not overridden, the child class should provide
|
bgneal@46
|
24 draw_cell.
|
bgneal@46
|
25 """
|
bgneal@46
|
26 def __init__(self):
|
bgneal@46
|
27 msg = 'CADrawer is an abstract type and should not be instantiated.'
|
bgneal@46
|
28 raise UnimplementedMethodException, msg
|
bgneal@46
|
29
|
bgneal@46
|
30 def draw(self, ca):
|
bgneal@46
|
31 """Draws a representation of cellular automaton (CA).
|
bgneal@46
|
32 This function generally has no visible effect."""
|
bgneal@46
|
33 raise UnimplementedMethodException
|
bgneal@46
|
34
|
bgneal@46
|
35 def draw_array(self, a):
|
bgneal@46
|
36 """Iterate through array (a) and draws any non-zero cells."""
|
bgneal@46
|
37 for i in xrange(self.rows):
|
bgneal@46
|
38 for j in xrange(self.cols):
|
bgneal@46
|
39 if a[i,j]:
|
bgneal@46
|
40 self.draw_cell(j, self.rows-i-1)
|
bgneal@46
|
41
|
bgneal@46
|
42 def draw_cell(self, ca):
|
bgneal@46
|
43 """Draws a single cell.
|
bgneal@46
|
44 Not required for all implementations."""
|
bgneal@46
|
45 raise UnimplementedMethodException
|
bgneal@46
|
46
|
bgneal@46
|
47 def show(self):
|
bgneal@46
|
48 """Displays the representation on the screen, if possible."""
|
bgneal@46
|
49 raise UnimplementedMethodException
|
bgneal@46
|
50
|
bgneal@46
|
51 def save(self, filename):
|
bgneal@46
|
52 """Saves the representation of the CA in filename."""
|
bgneal@46
|
53 raise UnimplementedMethodException
|
bgneal@46
|
54
|
bgneal@46
|
55
|
bgneal@46
|
56 class PyplotDrawer(Drawer):
|
bgneal@46
|
57 """Implementation of Drawer using matplotlib."""
|
bgneal@46
|
58
|
bgneal@46
|
59 def __init__(self):
|
bgneal@46
|
60 # we only need to import pyplot if a PyplotDrawer
|
bgneal@46
|
61 # gets instantiated
|
bgneal@46
|
62 global pyplot
|
bgneal@46
|
63 import matplotlib.pyplot as pyplot
|
bgneal@46
|
64
|
bgneal@46
|
65 def draw(self, ca, start=0, end=None):
|
bgneal@46
|
66 """Draws the CA using pyplot.pcolor."""
|
bgneal@46
|
67 pyplot.gray()
|
bgneal@46
|
68 a = ca.get_array(start, end)
|
bgneal@46
|
69 rows, cols = a.shape
|
bgneal@46
|
70
|
bgneal@46
|
71 # flipud puts the first row at the top;
|
bgneal@46
|
72 # negating it makes the non-zero cells black.
|
bgneal@46
|
73 pyplot.pcolor(-numpy.flipud(a))
|
bgneal@46
|
74 pyplot.axis([0, cols, 0, rows])
|
bgneal@46
|
75
|
bgneal@46
|
76 # empty lists draw no ticks
|
bgneal@46
|
77 pyplot.xticks([])
|
bgneal@46
|
78 pyplot.yticks([])
|
bgneal@46
|
79
|
bgneal@46
|
80 def show(self):
|
bgneal@46
|
81 """display the pseudocolor representation of the CA"""
|
bgneal@46
|
82 pyplot.show()
|
bgneal@46
|
83
|
bgneal@46
|
84 def save(self, filename='ca.png'):
|
bgneal@46
|
85 """save the pseudocolor representation of the CA in (filename)."""
|
bgneal@46
|
86 pyplot.savefig(filename)
|
bgneal@46
|
87
|
bgneal@46
|
88
|
bgneal@46
|
89 class PILDrawer(Drawer):
|
bgneal@46
|
90 """Implementation of Drawer using PIL and Swampy."""
|
bgneal@46
|
91
|
bgneal@46
|
92 def __init__(self, csize=4, color='black'):
|
bgneal@46
|
93 # we only need to import these modules if a PILDrawer
|
bgneal@46
|
94 # gets instantiated
|
bgneal@46
|
95 global Image, ImageDraw, ImageTk, Gui
|
bgneal@46
|
96 import Image
|
bgneal@46
|
97 import ImageDraw
|
bgneal@46
|
98 import ImageTk
|
bgneal@46
|
99 try:
|
bgneal@46
|
100 import Gui
|
bgneal@46
|
101 except ImportError:
|
bgneal@46
|
102 import swampy.Gui
|
bgneal@46
|
103 self.csize = csize
|
bgneal@46
|
104 self.color = color
|
bgneal@46
|
105
|
bgneal@46
|
106 def draw(self, ca, start=0, end=None):
|
bgneal@46
|
107 a = ca.get_array(start, end)
|
bgneal@46
|
108 self.rows, self.cols = a.shape
|
bgneal@46
|
109 size = [self.cols * self.csize, self.rows * self.csize]
|
bgneal@46
|
110
|
bgneal@46
|
111 self.gui = Gui.Gui()
|
bgneal@46
|
112 self.button = self.gui.bu(command=self.gui.quit)
|
bgneal@46
|
113
|
bgneal@46
|
114 self.image = Image.new(mode='1', size=size, color='white')
|
bgneal@46
|
115 self.drawable = ImageDraw.Draw(self.image)
|
bgneal@46
|
116 self.draw_array(numpy.flipud(a))
|
bgneal@46
|
117
|
bgneal@46
|
118 def draw_cell(self, i, j):
|
bgneal@46
|
119 size = self.csize
|
bgneal@46
|
120 x, y = i*size, j*size
|
bgneal@46
|
121 self.drawable.rectangle([x, y, x+size, y+size], fill=self.color)
|
bgneal@46
|
122
|
bgneal@46
|
123 def show(self):
|
bgneal@46
|
124 self.tkpi = ImageTk.PhotoImage(self.image)
|
bgneal@46
|
125 self.button.config(image=self.tkpi)
|
bgneal@46
|
126 self.gui.mainloop()
|
bgneal@46
|
127
|
bgneal@46
|
128 def save(self, filename='ca.gif'):
|
bgneal@46
|
129 self.image.save(filename)
|
bgneal@46
|
130
|
bgneal@46
|
131
|
bgneal@46
|
132 class EPSDrawer(Drawer):
|
bgneal@46
|
133 """Implementation of Drawer using encapsulated Postscript (EPS)."""
|
bgneal@46
|
134
|
bgneal@46
|
135 def __init__(self):
|
bgneal@46
|
136 self.cells = []
|
bgneal@46
|
137
|
bgneal@46
|
138 def draw(self, ca, start=0, end=None):
|
bgneal@46
|
139 a = ca.get_array(start, end)
|
bgneal@46
|
140 self.rows, self.cols = a.shape
|
bgneal@46
|
141 self.draw_array(a)
|
bgneal@46
|
142
|
bgneal@46
|
143 def draw_cell(self, i, j):
|
bgneal@46
|
144 self.cells.append((i,j))
|
bgneal@46
|
145
|
bgneal@46
|
146 def show(self):
|
bgneal@46
|
147 raise UnimplementedMethodException
|
bgneal@46
|
148
|
bgneal@46
|
149 def save(self, filename='ca.eps'):
|
bgneal@46
|
150 fp = open(filename, 'w')
|
bgneal@46
|
151 self.print_header(fp)
|
bgneal@46
|
152 self.print_outline(fp)
|
bgneal@46
|
153 self.print_cells(fp)
|
bgneal@46
|
154 self.print_footer(fp)
|
bgneal@46
|
155
|
bgneal@46
|
156 def print_cells(self, fp):
|
bgneal@46
|
157 for i, j in self.cells:
|
bgneal@46
|
158 fp.write('%s %s c\n' % (i, j))
|
bgneal@46
|
159
|
bgneal@46
|
160 def print_header(self, fp, size=0.9):
|
bgneal@46
|
161 fp.write('%!PS-Adobe-3.0 EPSF-3.0\n')
|
bgneal@46
|
162 fp.write('%%%%BoundingBox: -2 -2 %s %s\n' % (self.cols+2, self.rows+2))
|
bgneal@46
|
163
|
bgneal@46
|
164 fp.write('/c {\n')
|
bgneal@46
|
165 fp.write(' newpath moveto\n')
|
bgneal@46
|
166 fp.write(' 0 %g rlineto\n' % size)
|
bgneal@46
|
167 fp.write(' %g 0 rlineto\n' % size)
|
bgneal@46
|
168 fp.write(' 0 -%g rlineto\n' % size)
|
bgneal@46
|
169 fp.write(' closepath fill\n')
|
bgneal@46
|
170 fp.write('} def\n')
|
bgneal@46
|
171
|
bgneal@46
|
172 def print_outline(self, fp):
|
bgneal@46
|
173 fp.write('newpath 0.1 setlinewidth 0 0 moveto\n')
|
bgneal@46
|
174 fp.write('0 %s rlineto\n' % self.rows)
|
bgneal@46
|
175 fp.write('%s 0 rlineto\n' % self.cols)
|
bgneal@46
|
176 fp.write('0 -%s rlineto\n' % self.rows)
|
bgneal@46
|
177 fp.write('closepath stroke\n')
|
bgneal@46
|
178
|
bgneal@46
|
179 def print_footer(self, fp):
|
bgneal@46
|
180 fp.write('%%EOF\n')
|
bgneal@46
|
181
|