University of Virginia, Department of Computer Science
CS200: Computer Science, Spring 2002

Problem Set 3: L-System Fractals - Selected Answers

Honor Code Reminder: If you are currently taking CS200, it is a violation of the course pledge to look at Problem Set answers and discussions from previous years. Please don't do it.

Question 1: Show that (F1 O(R-60 F1) F1) is a string in the language defined by our BNF grammar. To do this, you should start with CommandSequence, and show a sequence of replacements that follow the grammar rules that produce the target string. You can use the rule numbers above to identify the rules.

There are many different replacement sequences that work. Here's one that always does the leftmost replacement first:

Start:    CommandSequence

Rule 1:   CommandSequence ==> ( CommandList )

Rule 2:   ( CommandList ) ==> ( Command CommandList )
            -----------         -------------------

Rule 4:   ( Command CommandList ) ==> ( FDistance Commandlist )
            -------                     ---------

Rule 7:   ( FDistance Commandlist ) ==> ( F1 CommandList )
             --------                      -

Rule 2:   ( F1 CommandList ) ==> ( F1 Command CommandList )
               -----------            -------------------

Rule 6:    ( F1 Command CommandList ) ==> ( F1 OCommandSequence CommandList )
                -------                        ----------------

Rule 1:    ( F1 OCommandSequence CommandList ) ==> (F1 O( CommandList ) CommandList )
                 ---------------                        ---------------

Rule 2:    (F1 O( CommandList ) CommandList ) ==> (F1 O( Command CommandList ) CommandList)
                  -----------                            -------------------

Rule 5:    (F1 O( Command CommandList ) CommandList) ==> (F1 O( RAngle CommandList ) CommandList)
                  -------                                       ------

Rule 8:    (F1 O( RAngle CommandList ) CommandList) ==> (F1 O( R-60 CommandList ) CommandList)
                   -----                                        ---

Rule 2:    (F1 O( R-60 CommandList ) CommandList) ==> (F1 O( R-60 Command CommandList ) CommandList)
                       -----------                                -------------------

Rules 4,7: (F1 O( R-60 Command CommandList ) CommandList) ==> (F1 O( R-60 F1 CommandList ) CommandList)
                       -------                                            --

Rule 3:    (F1 O( R-60 F1 CommandList ) CommandList) ==> (F1 O( R-60 F1 ) CommandList)
                          -----------                                  -

Rule 2:    (F1 O( R-60 F1 ) CommandList) ==> (F1 O( R-60 F1 ) Command CommandList)
                            -----------                       -------------------

Rules 4,7: (F1 O( R-60 F1 ) Command CommandList) ==> (F1 O( R-60 F1 ) F1 CommandList)
                            -------                                   --

Rule 3:    (F1 O( R-60 F1 ) F1 CommandList) ==> (F1 O( R-60 F1 ) F1)
                               -----------
Question 2: It will be useful to have procedures that take L-system commands as parameters, and return information about those commands. Define the following procedures:
  • (is-forward? lcommand) — evaluates to #t if the parameter passed is a forward command (indicated by its first element being a 'f tag).
  • (is-rotate? lcommand)
  • (is-offshoot? lcommand)
  • (get-distance lcommand) — evaluates to the distance associated with a forward command. Produces an error if the command is not a forward command (see below for how to produce an error).
  • (get-angle lcommand) — evaluates to the angle associated with a rotate command. Produces an error if the command is not a rotate command.
  • (get-offshoot-commands lcommand) — evaluates to the offshoot command list associated with an offshoot command. Produces an error if the command is not an offshoot command.

(define (is-forward? lcommand)
  (eq? (car lcommand) 'f))

(define (is-rotate? lcommand)
  (eq? (car lcommand) 'r))

(define (is-offshoot? lcommand)
  (eq? (car lcommand) 'o))

(define (get-distance lcommand)
  (if (is-forward? lcommand)
      (cdr lcommand)
      (error "Attempt to get distance from command that is not forward!")))

(define (get-angle lcommand)
  (if (is-rotate? lcommand)
      (cdr lcommand)
      (error "Attempt to get angle from command that is not rotate!")))

(define (get-offshoot-commands lcommand)
  (if (is-offshoot? lcommand)
      (cdr lcommand)
      (error "Attempt to get offshoot commands from command that is not an offshoot!")))

Question 3: Fill in the missing code for handling rotate commands (marked as Question 3 in the template code).

Question 4: Fill in the missing code for handling offshoot commands (marked as Question 4 in the template code).

(define (convert-to-curve lcommands)
  (if (null? lcommands)
      (lambda (t) (make-point 0.0 0.0)) ; the leaves (just a point for now)
      (if (is-forward? (car lcommands))
	  (connect-ends 
	   (make-vertical-line (get-distance (car lcommands)))
	   (convert-to-curve (cdr lcommands)))
	  (if (is-rotate? (car lcommands))
	      ;; L-system turns are clockwise, so we need to use - angle
	      (rotate-around-origin (convert-to-curve (cdr lcommands))
				    (- (get-angle (car lcommands))))
	      (if (is-offshoot? (car lcommands))
		  (connect-rigidly
		   (convert-to-curve (get-offshoot-commands (car lcommands)))
		   (convert-to-curve (cdr lcommands)))
		  (error "Bad lcommand!"))))))
Question 5: Define a procedure rewrite-lcommands that takes a list of L-system commands as its first parameter. The second parameter is a list of L-system commands that should replace every forward command in the first list of commands in the result.

Complete the definition of rewrite-lcommands.

(define (rewrite-lcommands lcommands replacement)
  (flatten-commands
   (map 
    (lambda (command)  
      (if (is-forward? command)
	  replacement ; forwards are replaced with replacement 
	  (if (is-offshoot? command)
	       ; we need to replace all forwards in an offshoot command also	
	      (make-offshoot-command 
	       (rewrite-lcommands (get-offshoot-commands command)
				  replacement))
	      (if (is-rotate? command)
		  command ; rotates are unchanged
		  (error "Bad command: " command)))))
    lcommands)))


Question 6: Define a procedure make-lsystem-fractal that takes three parameters: replace-commands, a list of L-system commands that replace forward commands in the rewriting; start, a list of L-system commands that describes the starting curve; level, the number of iterations to apply the rewrite rule.

Hint: the n-times function you defined in PS2 might be useful.

Everyone who got this to work did it without using n-times. The tough part of using n-times, is we cannot use replace-commands directly since is takes two parameters. When n-times composes functions, it passes the output of one application as the input of the next application, so there can only be one parameter. Hence, we need to make a new procedure that does the command replacements but only takes one parameter. Here's how:

(define (make-lsystem-fractal replace-commands start level)
  ((n-times
    (lambda (previous-commands)
      (rewrite-lcommands previous-commands replace-commands))
    level)
   start))
Question 7: Draw some fractals by playing with the L-system commands. Try changing the rewrite rule, the starting commands, level and leaf curve (in convert-to-curve) to draw an interesting fractal. Turn in the code you used, as well as a printout of the display window.

The code I used to make the lambda tree of knowledge is lambdatree.ss. It also uses colored-curve.ss to draw curves in color.


CS 655 University of Virginia
Department of Computer Science
CS 200: Computer Science
David Evans
evans@virginia.edu
Using these Materials