[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' {

     case item=3D'-' {

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).



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',
   def __init__(self,vPos=3DVector((0,0,0)),
                     vH  =3DVector((0,0,1)),
                     vL  =3DVector((0,1,0)),
                     vU  =3DVector((1,0,0))):
       """The turtle will start at the origin with the
       Z-axis as forward direction and Y-axis as left
       direction. (lparser.txt)"""
       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 ""
       for item in instructions:

           if getparam:          # getting parameter?
               if item =3D=3D ")":
                   getparam =3D 0  # done getting
                   command =3D command + "(" + param + ")"
                   param =3D param + item  # building parameter
           if checkparam:        # checking for parameter?
               checkparam =3D 0
               if item =3D=3D "(":
                   param =3D ""
                   getparam =3D 1  # parameter exists
                   command =3D command + "()" # no parameter
                   eval(command)               =20

           # initializing command string
           command =3D "self." + self.instrudict.get(item)
           checkparam =3D 1    # set flag

           if checkparam:
              command =3D command + "()" # no parameter
   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
   # other functions to follow (as per instrudict)=20


 >>> 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