Teaching Python
with Graphics
by Kirby Urner
OSCON 2004 2
The Challenge
How might we generate interesting
graphics while learning to program
in core Python?
OSCON 2004 3
Pitfalls
• Getting lost in an elaborate API that takes
attention away from core Python
• Too much code for le...
OSCON 2004 4
Some Solutions
• Wrap the graphics API, or use just a few of
an application’s features
• Recycle code: use cl...
OSCON 2004 5
Types of Graphics
with sample apps & libraries
Still Moving
PIL
Tk
wxWidgets
PyGame
POV-Ray
POV-Ray
VPython
P...
OSCON 2004 6
Example: Using POV-Ray
• Simplify API: edge,
point, face
• Gradually build up a
generic Vector class
• Source...
OSCON 2004 7
Downloaded Polyhedron Data
http://www.netlib.org/polyhedra/
:name
Tetrahedron
...
:net
4 3
3 1 2 4
3 3 1 4
3 ...
OSCON 2004 8
OSCON 2004 9
A Core Python Exercise
:name
Tetrahedron
...
:net
4 3
3 1 2 4
3 3 1 4
3 1 0 2
3 4 2 5
:solid
4 3
3 7 9 8
3 6 ...
OSCON 2004 10
One Solution
Get edges by zipping each face-list with rotation thereof:
zip([7, 8, 9], [8, 9, 7]) ((7, 8), (...
OSCON 2004 11
Standard Core Python…
def getedges (faces):
"""
Return unique edges from a list of
faces -- edges(f) for rot...
OSCON 2004 12
List, Tuple, Dictionary, Set, String
vertices = {}
for i in range(numverts): # computed earlier
coords = f.r...
OSCON 2004 13
A POV-Ray Writer
class Povray (object):
"""Skeletal outline of a POV-Ray writer object" " "
def __init__ (se...
OSCON 2004 14
OSCON 2004 15
Simple API Doesn’t Mean
Simple Shapes
Waterman Polyhedra: generated from same algorithm, single integer argu...
OSCON 2004 16
Simple API: Example OutputPOINT:
sphere {<-0.5000000000022, -0.2886751345948, -0.8164965809277>
0.02 texture...
OSCON 2004 17
Core Python: String Substitution
def point (self, v0):
s0 = "<%r, %r, %r>" % v0.xyz
self.output.write("spher...
OSCON 2004 18
Point, Edge, Face = Reusable API
def face (self, vlist) :
self.output.write("polygon{%s,n" % (len(vlist)+1,)...
OSCON 2004 19
VRML Class: Same API
OSCON 2004 20
Vector Module
• How much detail depends on
mathematical needs, interests, goals of
student
• Prime example o...
OSCON 2004 21
Going from 2D to 3D
>>> from vector import Vector2d, Vector3d
>>> [ f for f in dir(Vector2d) if not '__' in ...
OSCON 2004 22
Python is Interactive!
>>> v0 = Vector3d(1,1,0)
>>> v0
Vector (1, 1, 0)
>>> v0.rotate(90,axis='x')
Vector (1...
OSCON 2004 23
Graphing for Free
def graphit(pairs, output):
"""
Connect (x,f(x)) pairs with edges,
writing to output
"""
e...
OSCON 2004 24
Function: zip(x, y)
from math import cos
def f(x): return cos(4*x)
def test ():
thedomain = [i/10. for i in ...
OSCON 2004 25
OSCON 2004 26
Final Puzzle Piece:
A Shape Class
class Shape:
def __init__ (self, faces, points):
def translate (self, v):
...
OSCON 2004 27
Initialize Shape with a Face List
and Dictionary of Points
class Shape:
def __init__ (self, faces, points):
...
OSCON 2004 28
Translate Method
def translate (self, v):
"""
Translate by Vector v
Return a new shape
"""
newp = {}
for p i...
OSCON 2004 29
Rotate Method
def rotate (self, degrees, axis):
"""
Rotate in place: center, rotate, move back
Return a new ...
OSCON 2004 30
Display Method
def display (self, output):
"""
Invoke edge, face and point methods of whatever
write object ...
OSCON 2004 31
Core Python Topics Explored
• Edges from Faces: data structures, import
• String substitution: POV-Ray metho...
OSCON 2004 32
Another Example:
Fractals with PIL
Mandelbrot Set Julia Set
OSCON 2004 33
Write to PNG Pixel by Pixel
import Image # PIL installed under site-packages
…
class Julia:
…
def newimage (...
OSCON 2004 34
“Divergence velocity”
drives gray scale
class Julia:
…
def fractal (self,x,y): # looping thru x,y rectangle
...
OSCON 2004 35
Mandelbrot as a subclass of Julia
class Mandelbrot (Julia):
""" Entire class – all else inherited from Julia...
OSCON 2004 36
Summay of PIL example
• Focus is on mathematics: PIL does not
get in the way
• Core algorithm is simple, res...
OSCON 2004 37
Another Example:
Cellular Automata with Tk
Demonstration using John Zelle’s
graphics.py* – a wrapper for
Tki...
OSCON 2004 38
Cellular Automata with Tk
Demonstration using John Zelle’s
graphics.py* – a wrapper for
Tkinter simplifying ...
OSCON 2004 39
Simple Interface
# from Dr. Zelle's graphics library
from graphics import GraphWin, Point, Rectangle
class C...
OSCON 2004 40
Motion: Three Demonstrations
• Python + POV-Ray with a built-in “clock”
generates animations, including mpeg...
OSCON 2004 41
Graphics in PythonCard
Demonstration using PythonCard *
– a wrapper for wxWidgets
simplifying the API
* Sour...
OSCON 2004 42
Thank You
• Contact:
Kirby Urner
kirby@4dsolutions.net
• These slides and source code:
http://www.4dsolution...
Upcoming SlideShare
Loading in …5
×

Pythonic Graphics

1,688 views

Published on

OSCON 2004

Published in: Technology, Design
0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,688
On SlideShare
0
From Embeds
0
Number of Embeds
21
Actions
Shares
0
Downloads
0
Comments
0
Likes
5
Embeds 0
No embeds

No notes for slide

Pythonic Graphics

  1. 1. Teaching Python with Graphics by Kirby Urner
  2. 2. OSCON 2004 2 The Challenge How might we generate interesting graphics while learning to program in core Python?
  3. 3. OSCON 2004 3 Pitfalls • Getting lost in an elaborate API that takes attention away from core Python • Too much code for learning purposes (smallish projects required) • Resulting graphics are trivial and/or uninteresting
  4. 4. OSCON 2004 4 Some Solutions • Wrap the graphics API, or use just a few of an application’s features • Recycle code: use classes to isolate API- specific metheds from generic methods • Look for reusable data in text format and/or use simple algorithms that generate complex patterns
  5. 5. OSCON 2004 5 Types of Graphics with sample apps & libraries Still Moving PIL Tk wxWidgets PyGame POV-Ray POV-Ray VPython PyOpenGL Flat Spatial
  6. 6. OSCON 2004 6 Example: Using POV-Ray • Simplify API: edge, point, face • Gradually build up a generic Vector class • Sources of polyhedra on the web
  7. 7. OSCON 2004 7 Downloaded Polyhedron Data http://www.netlib.org/polyhedra/ :name Tetrahedron ... :net 4 3 3 1 2 4 3 3 1 4 3 1 0 2 3 4 2 5 :solid 4 3 3 7 9 8 3 6 7 8 3 7 6 9 3 8 9 6 :vertices 10 6 -1[-1] -.577350269189626[(-1/3)*sqrt(3)] 0[0] -.5[-1/2] .288675134594813[(1/6)*sqrt(3)] 0[0] 0[0] -.577350269189626[(-1/3)*sqrt(3)] 0[0] 0[0] 1.15470053837925[(2/3)*sqrt(3)] 0[0] .5[1/2] .288675134594813[(1/6)*sqrt(3)] 0[0] 1[1] -.577350269189626[(-1/3)*sqrt(3)] 0[0] -1.333333333333 -.76980035891950045 -1.0886621079036349 -.8333333333384 -1.0584754935143137 -.27216552697590904 -.500000000002 -.28867513459481264 -.81649658092772612 -.4444444444495 -1.2188505682892098 -1.1793839502289383 :EOF
  8. 8. OSCON 2004 8
  9. 9. OSCON 2004 9 A Core Python Exercise :name Tetrahedron ... :net 4 3 3 1 2 4 3 3 1 4 3 1 0 2 3 4 2 5 :solid 4 3 3 7 9 8 3 6 7 8 3 7 6 9 3 8 9 6 From a list of faces, generate a list of unique edges: Input: [(7,8,9), (6,7,8), (7,6,9), (8, 9, 6)] Output: [(6, 8), (6, 9), (7, 9), (7, 8), (8, 9), (6, 7)]
  10. 10. OSCON 2004 10 One Solution Get edges by zipping each face-list with rotation thereof: zip([7, 8, 9], [8, 9, 7]) ((7, 8), (8, 9), (9, 7)) Sort resulting edges: for p in pairs: e = list(p) e.sort() (Python 2.4: e = sorted(list(p)) Add edges to set: edges.add( tuple(e) ) (adds only unique e)
  11. 11. OSCON 2004 11 Standard Core Python… def getedges (faces): """ Return unique edges from a list of faces -- edges(f) for rotate/zip) """ alledges = sets.Set() for f in faces: for p in edges( f ): e = list( p ) e.sort( ) alledges.add( tuple(e) ) return list(alledges)
  12. 12. OSCON 2004 12 List, Tuple, Dictionary, Set, String vertices = {} for i in range(numverts): # computed earlier coords = f.readline().split() strs = [] for c in coords: strs.append(c.split('[')[0]) # ignore [xxx] vertices[i] = tuple( [float(x) for x in strs] ) return vertices :vertices 10 6 -1[-1] -.577350269189626[(-1/3)*sqrt(3)] 0[0] -.5[-1/2] .288675134594813[(1/6)*sqrt(3)] 0[0] … -.500000000002 -.28867513459481264 -.81649658092772612 -.4444444444495 -1.2188505682892098 -1.1793839502289383 :EOF
  13. 13. OSCON 2004 13 A POV-Ray Writer class Povray (object): """Skeletal outline of a POV-Ray writer object" " " def __init__ (self, filename): self.output = file(filename, 'w') self.header() def header (self): pass def edge (self, v0, v1): pass def face (self, vlist): pass def point (self, v0): pass def close (self): self.output.close()
  14. 14. OSCON 2004 14
  15. 15. OSCON 2004 15 Simple API Doesn’t Mean Simple Shapes Waterman Polyhedra: generated from same algorithm, single integer argument Open source Qhull utility also used to generate these polyhedra
  16. 16. OSCON 2004 16 Simple API: Example OutputPOINT: sphere {<-0.5000000000022, -0.2886751345948, -0.8164965809277> 0.02 texture{pigment {color Gold}} cylinder {<-0.5000000000022, -0.2886751345948, -0.8164965809277>, <-0.4444444444497, -1.218850568289, -1.179383950228> 0.02 texture{pigment {color Gold}}} EDGE: polygon{ 4, <-0.8333333333381, -1.058475493514, -0.2721655269759>, <-0.4444444444497, -1.218850568289, -1.179383950228>, <-0.5000000000022, -0.2886751345948, -0.8164965809277>, <-0.8333333333381, -1.058475493514, -0.2721655269759> texture{pigment {color Cyan}}} FACE:
  17. 17. OSCON 2004 17 Core Python: String Substitution def point (self, v0): s0 = "<%r, %r, %r>" % v0.xyz self.output.write("sphere {%s %s texture{pigment {color %s}}}n“ % (s0, self.sphradius, self.sphcolor)) def edge (self, v0, v1): s0 = "<%r, %r, %r>" % v0.xyz s1 = "<%r, %r, %r>" % v1.xyz self.output.write("cylinder {%s, %s %s texture{pigment {color %s}}}n" % (s0, s1, self.cylradius, self.cylcolor))
  18. 18. OSCON 2004 18 Point, Edge, Face = Reusable API def face (self, vlist) : self.output.write("polygon{%s,n" % (len(vlist)+1,)) for v in vlist: self.output.write("<%r, %r, %r>,n" % v.xyz) self.output.write("<%r, %r, %r>n" % vlist[0].xyz) self.output.write("texture{pigment {color %s}}}n" % (self.facecolor)) Same interface with rewritten methods for VRML, LiveGraphics3D, VPython…
  19. 19. OSCON 2004 19 VRML Class: Same API
  20. 20. OSCON 2004 20 Vector Module • How much detail depends on mathematical needs, interests, goals of student • Prime example of where operator overloading makes so much sense • Easy to play with interactively (in shell), even if the code is pre-written (many examples of “vectors.py” out there)
  21. 21. OSCON 2004 21 Going from 2D to 3D >>> from vector import Vector2d, Vector3d >>> [ f for f in dir(Vector2d) if not '__' in f ] ['dot', 'mag', 'norm', 'polar', 'rotate'] >>> [ f for f in dir(Vector3d) if not '__' in f ] ['cross', 'dot', 'mag', 'norm', 'rotate', 'spherical'] >>> help(Vector3d.__mul__) Help on method __mul__: __mul__(self, other) unbound vector.Vector3d method Might be just scalar multiplication, but if the arg is another vector, let's go through the motions of quaternion * quaternion
  22. 22. OSCON 2004 22 Python is Interactive! >>> v0 = Vector3d(1,1,0) >>> v0 Vector (1, 1, 0) >>> v0.rotate(90,axis='x') Vector (1.0, 2.22044604925e-016, 1.0) >>> v0.rotate(90,axis='y') Vector (2.22044604925e-016, 1.0, -1.0) >>> v0.spherical() (1.4142135600000001, 90.0, 45.0) >>> v0.rotate(90,axis='y').spherical() (1.4142135600000001, 135.0, 90.0)
  23. 23. OSCON 2004 23 Graphing for Free def graphit(pairs, output): """ Connect (x,f(x)) pairs with edges, writing to output """ edges = zip(pairs[:-1], pairs[1:]) for e in edges: v0 = apply(Vector3d, e[0]) v1 = apply(Vector3d, e[1]) output.edge(v0, v1)
  24. 24. OSCON 2004 24 Function: zip(x, y) from math import cos def f(x): return cos(4*x) def test (): thedomain = [i/10. for i in range(-20,20)] therange = [f(x) for x in thedomain] myfile = POVray("thegraph.pov") axes.xyz(myfile) # draws/labels XYZ axes myfile.cylcolor = 'Red' graphit( zip(thedomain, therange), myfile) myfile.close()
  25. 25. OSCON 2004 25
  26. 26. OSCON 2004 26 Final Puzzle Piece: A Shape Class class Shape: def __init__ (self, faces, points): def translate (self, v): def rotate (self, degrees, axis): def getcenter (self): def center (self): def scale (self, factor): def __repr__ (self): def display (self, output):
  27. 27. OSCON 2004 27 Initialize Shape with a Face List and Dictionary of Points class Shape: def __init__ (self, faces, points): self.faces = faces self.points = points self.edges = getedges(self.faces) self.showfaces = True self.showedges = True self.showpoints = True povout = POVray("myfile7.pov") # Povray writer theshape = Shape(faces, points) # from file theshape = theshape.center() # cuz it wasn’t theshape = theshape.rotate(30,'y') theshape.display(povout) # writer is argument povout.close()
  28. 28. OSCON 2004 28 Translate Method def translate (self, v): """ Translate by Vector v Return a new shape """ newp = {} for p in self.points: newp[p] = self.points[p] + v return Shape(self.faces, newp) Note: could use __add__ as a synonym, and __mul__ for scale method
  29. 29. OSCON 2004 29 Rotate Method def rotate (self, degrees, axis): """ Rotate in place: center, rotate, move back Return a new shape """ oldcenter = self.getcenter() # home newshape = Shape(self.faces, self.points) newshape = newshape.translate(-oldcenter) # to origin for p in newshape.points: newshape.points[p] = newshape.points[p].rotate(degrees, axis) newshape = newshape.translate(oldcenter) # to home return newshape Could be enhance to rotate around any vector, not just x,y or z axis (entails a change in the Vector class)
  30. 30. OSCON 2004 30 Display Method def display (self, output): """ Invoke edge, face and point methods of whatever write object (API as all shape knows about) """ if self.showedges: for e in self.edges: v0,v1 = [self.points[i] for i in e] output.edge(v0,v1) if self.showfaces: for f in self.faces: vlist = [self.points[i] for i in f] output.face(vlist) if self.showpoints: for v0 in self.points.values(): output.point(v0)
  31. 31. OSCON 2004 31 Core Python Topics Explored • Edges from Faces: data structures, import • String substitution: POV-Ray methods • Classes: POV-Ray writer, Shape • Operator Overloading: Vector class • File I/O: parsing input, writing output
  32. 32. OSCON 2004 32 Another Example: Fractals with PIL Mandelbrot Set Julia Set
  33. 33. OSCON 2004 33 Write to PNG Pixel by Pixel import Image # PIL installed under site-packages … class Julia: … def newimage (self): self.im = Image.new('RGB',self.size) def compute (self): print "Computing %s..." % self.__class__.__name__ self.newimage() for x in range(self.size[0]): for y in range(self.size[1]): xp,yp = self.getcoords(x,y) dotcolor = self.fractal(xp,yp) # core self.im.putpixel((x,y), dotcolor) # !! self.saveimage()
  34. 34. OSCON 2004 34 “Divergence velocity” drives gray scale class Julia: … def fractal (self,x,y): # looping thru x,y rectangle n = self.n z = self.z # fixed in Julia o = complex(x,y) # the variable (location) dotcolor = (0,0,0) # default, convergent for trials in range(n): if abs(o) <= 2.0: o = o**2 + z else: dotcolor = tuple([ int(1 + 254*trials/n) for i in range(3)]) break # diverged return dotcolor
  35. 35. OSCON 2004 35 Mandelbrot as a subclass of Julia class Mandelbrot (Julia): """ Entire class – all else inherited from Julia """ def fractal (self,x,y): # looping thru x,y rectangle n = self.n z = complex(x,y) # now z is what’s variable o = complex(0,0) # always starts at 0 dotcolor = (0,0,0) # default, convergent for trials in range(n): if abs(o) <= 2.0: o = o**2 + z else: dotcolor = tuple([ int(1 + 254*trials/n) for i in range(3)]) break # diverged return dotcolor
  36. 36. OSCON 2004 36 Summay of PIL example • Focus is on mathematics: PIL does not get in the way • Core algorithm is simple, result is complex • Python may clarify mathematical relationships (Mandelbrot as subclass of Julia)
  37. 37. OSCON 2004 37 Another Example: Cellular Automata with Tk Demonstration using John Zelle’s graphics.py* – a wrapper for Tkinter simplifying the API * Source is GPL and available at http://mcsp.wartburg.edu/zelle/python/
  38. 38. OSCON 2004 38 Cellular Automata with Tk Demonstration using John Zelle’s graphics.py* – a wrapper for Tkinter simplifying the API * Source is GPL and available at http://mcsp.wartburg.edu/zelle/python/
  39. 39. OSCON 2004 39 Simple Interface # from Dr. Zelle's graphics library from graphics import GraphWin, Point, Rectangle class Canvas(object): def __init__(self, width, rows, ps): # ps = pixelsize self.ps = ps self.c = GraphWin('NKS', width*ps, rows*ps, False) self.c.setBackground('black') def drawcell (self, p): therow, thecol = p[0] * self.ps, p[1] * self.ps therect = Rectangle( Point(therow, thecol), Point(therow + self.ps-1, thecol + self.ps -1)) therect.setFill('yellow') therect.setOutline('yellow') therect.draw(self.c)
  40. 40. OSCON 2004 40 Motion: Three Demonstrations • Python + POV-Ray with a built-in “clock” generates animations, including mpeg clips • VPython provides an API to OpenGL for real time experience (similar to VRML) – PyGeo an example of an app that wraps VPython • PyGame provides an API to 2D sprites, with collision detection and sound
  41. 41. OSCON 2004 41 Graphics in PythonCard Demonstration using PythonCard * – a wrapper for wxWidgets simplifying the API * Source is GPL and available at http://pythoncard.sourceforge.net/
  42. 42. OSCON 2004 42 Thank You • Contact: Kirby Urner kirby@4dsolutions.net • These slides and source code: http://www.4dsolutions.net/oscon2004/ (and via official OSCON site)

×