[Edu-sig] Re: Implementing an Lsystem (edu-sig Python project)
Kirby Urner
pdx4d@teleport.com
Thu, 06 Jul 2000 23:10:40 -0700
Note: This is Part 3 of 3 (last in a series)
Part 1: http://www.python.org/pipermail/edu-sig/2000-July/000545.html
Part 2: http://www.python.org/pipermail/edu-sig/2000-July/000546.html
As outlined in previous posts, the Lsystem formalism provides=20
long strings of single-letter turtle commands, e.g. 'FF--FF++&^FF'
In Lapr=E9's version, commands may be followed by a parameter enclosed=20
in parentheses, e.g. 'F(10)F--(90)FF++(100)'
In Lapr=E9's C implementation, you find a long set of case-break=20
statements for parsing through these command strings i.e. we=20
check for=20
case item=3D'F' {
dostuff;
break;}
case item=3D'-' {
dostuff;
break;}
and so forth. =20
Given the long list of single letter commands (about 30 of them),=20
it makes sense to use a Python dictionary construct coupled with=20
eval(). To each symbol there corresponds the name of a turtle=20
method, saved as a string. The turtle knows how to convert=20
returned strings to method names at runtime. Also, the turtle=20
knows to hold off evaluating a command until it has checked for=20
the optional following parameter.
In my first post in this series, I provided the skeletal outline=20
of an Lturtle class. Besides being very incomplete, it contained=20
syntactical errors and would not have imported.
The Lturtle class below will compile, plus it accepts long=20
instruction strings via it's "chomp" method. Chomp "eats its=20
way through" the instructions, turning switches on and off=20
as it tests for "(" and ")" -- the parameter symbols. It=20
looks up symbols (other than parentheses) in the instrudict=20
class variable (a dictionary), and builds a command to=20
evaluate -- e.g. self.turnleft(30) -- once parameter checking=20
is complete. =20
Others will likely come up with simpler versions of "chomp"=20
-- some won't like my switch-based approach.
I've only coded stub functions for command symbols f + and -=20
(even though the dictionary is somewhat more complete). The=20
point is to test the design. With all of these pieces in=20
place, we've set the stage to write an Lsystem parser in Python,=20
i.e. to read in the same files Lapr=E9's Lparser.exe accepts,=20
and to have the output go to Povray for rendering (or other=20
formats, depending how ambitious we are -- how many wheels=20
we want to reinvent). I've got more turtle-related code,=20
in connection with Povray in particular, at=20
http://www.inetarena.com/~pdx4d/ocn/numeracy3.html -- lots
more clues about how to proceed in this essay.
Whether or not I'll go further down this road myself I don't=20
know right now. If I do, I'll post further details to the=20
web. In the meantime, this might be an interesting project=20
for others to continue and/or adapt to classroom situations.
There's lots of literature, positively reinforcing graphics,
plus a synergy of two topics already covered at the high school=20
level in many curricula: fractal patterns and turtle graphics. =20
Plus the recursive production rules are good for reinforcing=20
that algorithms may be alpha-numeric, not just numeric -- i.e.=20
string and symbol processing is every bit as "mathematical"=20
(potentially), as anything to do with "raw number crunching"=20
(as algebraic manipulation so well demonstrates).
Kirby
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
lsystems.py
from coords import Vector
class Lturtle:
stackstate =3D [] # remembers saved state
delta =3D 0 # angle of rotation
length =3D 0.5 # full length of turtle move
thickness =3D 0.02 # default thickness of cylinder
instrudict =3D {'+':'turnleft',
'-':'turnright',
'&':'pitchdown',
'^':'pitchup',
'<':'leftroll',
'>':'rightroll',
'|':'turn180',
'%':'roll180',
'$':'rollhoriz',
'x':'randturn',
't':'gravity',
'F':'fdraw',
'f':'fnodraw',
'Z':'halfdraw',
'z':'halfnodraw',
'g':'Fnorecord',
'.':'Nomove'}
=20
def __init__(self,vPos=3DVector((0,0,0)),
vH =3DVector((0,0,1)),
vL =3DVector((0,1,0)),
vU =3DVector((1,0,0))):
=20
"""The turtle will start at the origin with the
Z-axis as forward direction and Y-axis as left
direction. (lparser.txt)"""
=20
self.vPos,self.vH,self.vL,self.vU =3D vPos,vH,vL,vU
def chomp(self,instructions):
getparam =3D 0
checkparam =3D 0
param =3D ""
=20
for item in instructions:
if getparam: # getting parameter?
if item =3D=3D ")":
getparam =3D 0 # done getting
command =3D command + "(" + param + ")"
eval(command)
continue
else:
param =3D param + item # building parameter
continue
=20
if checkparam: # checking for parameter?
checkparam =3D 0
if item =3D=3D "(":
param =3D ""
getparam =3D 1 # parameter exists
continue
else:
command =3D command + "()" # no parameter
eval(command) =20
# initializing command string
command =3D "self." + self.instrudict.get(item)
checkparam =3D 1 # set flag
else:
if checkparam:
command =3D command + "()" # no parameter
eval(command)
=20
def fnodraw(self,n=3D""):
print "Forward %s (no draw)" % n
def turnleft(self,n=3D""):
print "Turn Left around vU %s" % n
def turnright(self,n=3D""):
print "Turn Right around vU %s" % n
=20
# other functions to follow (as per instrudict)=20
Usage:
>>> reload(lsystems)
<module 'lsystems' from 'G:\PYTHON16\ocn\lsystems.py'>
>>> lturt =3D lsystems.Lturtle()
>>> lturt.chomp('f--f--f--f++')
Forward (no draw)
Turn Right around vU=20
Turn Right around vU=20
Forward (no draw)
Turn Right around vU=20
Turn Right around vU=20
Forward (no draw)
Turn Right around vU=20
Turn Right around vU=20
Forward (no draw)
Turn Left around vU=20
Turn Left around vU=20
>>> lturt.chomp('f-(90)-f--f-(-105)-f++(100)')
Forward (no draw)
Turn Right around vU 90
Turn Right around vU=20
Forward (no draw)
Turn Right around vU=20
Turn Right around vU=20
Forward (no draw)
Turn Right around vU -105
Turn Right around vU=20
Forward (no draw)
Turn Left around vU=20
Turn Left around vU 100