Fundamentals of Computer Science I: Media Computing (CS151.02 2007F)

Assignment 15: Exploring Fractal-Like Images


Due: 4 p.m., Tuesday, 20 November 2007

Summary: In this assignment, you will explore some potentially interesting variants of a technique for designing images called fractals.

Purposes: To help you think more about deep recursion (a technique in which you do multiple recursive calls per procedure call). To give you one final chance to explore techniques for making images before you begin your project.

Expected Time: Two to three hours.

Collaboration: We encourage you to work in groups of size three. You may, however, work alone or work in a group of size two or size four. You may discuss this assignment with anyone, provided you credit such discussions when you submit the assignment.

Submitting: Email your answer to . The title of your email should have the form CSC151.02 2007F Assignment 15 and should contain your answers to all parts of the assignment. Scheme code should be in the body of the message.

Warning: So that this assignment is a learning experience for everyone, we may spend class time publicly critiquing your work.

Preliminaries

One of the more mathematically interesting kinds of drawings is what is commonly called a fractal. Fractals are self-similar drawings. That is, each portion of the drawing bears some resemblance to the larger drawing. We normally draw fractals by breaking the larger drawing into equal portions and drawing each portion using the same technique.

For example, to draw an NxM rectangle, we might draw nine (N/3)x(M/3) rectangles in a grid. Similarly, to draw each of those nine rectangles, we might draw nine (N/9)x(M/9) rectangles, and so on and so forth. When do we stop? When we've recursed enough of when the rectangles are small enough.

We might express this technique in code as follows.

;;; Procedure:
;;;   fractal-rectangle!
;;; Parameters:
;;;   image, an image
;;;   color, the desired color of the rectangle
;;;   left, the left edge of the rectangle
;;;   top, the top edge of the rectangle
;;;   width, the width of the rectangle
;;;   height, the height of the rectangle
;;;   level, the level of recursion
;;; Purpose:
;;;   Draw a "fractal" version of the rectangle by
;;;   breaking the rectangle up into subrectangles,
;;;   and recursively drawing some of those rectangles
;;;   (potentially in different colors).  When does
;;;   recursion stop?  When the level of recursion is 0.
;;; Produces:
;;;   [Nothing; Called for the side effect]
(define fractal-rectangle!
  (lambda (image color left top right bottom level)
    (cond
      ((= level 0)
       (envt.set-fgcolor! color)
       (image.select-rectangle! image selection.replace
                                left top 
                                (- right left)
                                (- bottom top))
       (image.fill! image)
       (image.select-nothing! image)
       (envt.update-displays!))
      (else
       (let* ((midcol1 (round (+ left (/ (- right left) 3))))
              (midcol2 (round (- right (/ (- right left) 3))))
              (midrow1 (round (+ top (/ (- bottom top) 3))))
              (midrow2 (round (- bottom (/ (- bottom top) 3)))))
         ; First row of squares
         (fractal-rectangle! image 
                             color
                             left top 
                             midcol1 midrow1
                             (- level 1))
         (fractal-rectangle! image 
                             color
                             midcol1 top 
                             midcol2 midrow1
                             (- level 1))
         (fractal-rectangle! image 
                             color
                             midcol2 top 
                             right midrow1
                             (- level 1))
         ; Second row of squares
         (fractal-rectangle! image 
                             color
                             left midrow1
                             midcol1 midrow2
                             (- level 1))
         (fractal-rectangle! image 
                             color
                             midcol1 midrow1
                             midcol2 midrow2
                             (- level 1))
         (fractal-rectangle! image 
                             color
                             midcol2 midrow1
                             right midrow2
                             (- level 1))
         ; Third row of squares
         (fractal-rectangle! image 
                             color
                             left midrow2
                             midcol1 bottom
                             (- level 1))
         (fractal-rectangle! image 
                             color
                             midcol1 midrow2
                             midcol2 bottom
                             (- level 1))
         (fractal-rectangle! image 
                             color
                             midcol2 midrow2
                             right bottom
                             (- level 1))
         )))))

Why would we use such a technique, since all we end up with is the same rectangle? Well, things get a bit interesting when you make subtle changes (other than just the level of recursion) at each recursive call. Most typically, you might draw the different subrectangles in modified versions of the original color. Once you do that, you can also think about changing whether or not you use an even grid, or even whether or not you draw each sub-rectangle.

The technique sounds simple, but it can produce some very interesting images. More generally, fractals also let us provide interesting simulations of many natural objects, such as trees, mountains, and coastlines, that have some of the same self-similarity. We'll start our explorations with these rectangles.

Preparation

In order to best understand how fractal drawing of rectangles works, it will be useful for you to practice a bit with variants of the fractal-rectangle! procedure.

a. We'll start with the plain version of the procedure. Create a 200x200 image, call it canvas, and use fractal-rectangle! to draw a 200x200 blue rectangle using a recursion level of 0.

b. Next, use fractal-rectangle! to draw a 200x200 red rectangle using a recursion level of 1. (Don't be surprised if it looks pretty boring.)

c. Next, use fractal-rectangle! to draw a 200x200 green rectangle using a recursion level of 2. (Again, don't be surprised if it's boring.)

d. After those three exercises, you are probably ready to do something a bit more interesting. Change the recursive calls for the top-middle, left-middle, right-middle, and bottom-middle subrectangles so that they use the complement of the color. Then, draw a 200x200 rectangle fractal with black as the initial color and a recursion level of 1. Once you've done that, try it with a recursion level of 2.

e. Predict what will happen if you draw a 200x200 rectangle with yellow as the initial color and a recursion level of 3. Check your prediction experimentally.

f. Here is a procedure that averages two colors.

;;; Procedure:
;;;   rgb.average
;;; Parameters:
;;;   color1, an RGB color
;;;   color2, an RGB color
;;; Purpose:
;;;   Averages color1 and color2
;;; Produces:
;;;   average, an RGB color
;;; Preconditions:
;;;   [None; called for the side effect]
;;; Postconditions:
;;;   (rgb.red average) is the average of 
;;;     (rgb.red color1) and (rgb.red color2)
;;;   (rgb.green average) is the average of 
;;;     (rgb.green color1) and (rgb.green color2)
;;;   (rgb.blue average) is the average of 
;;;     (rgb.blue color1) and (rgb.blue color2)
(define rgb.average
  (lambda (color1 color2)
    (rgb.new (* 0.5 (+ (rgb.red color1) (rgb.red color2)))
             (* 0.5 (+ (rgb.green color1) (rgb.green color2)))
             (* 0.5 (+ (rgb.blue color1) (rgb.blue color2))))))

Rewrite fractal-rectangle! so that the colors in the top-middle, left-middle, right-middle, and bottom-middle subrectangles are averaged with black and the colors in the other five subrectangles are averaged with white.

g. What do you expect to have happen if we draw a 200x200 level-2 fractal rectangle whose initial color is blue? Check your answer experimentally.

h. What do you expect to have happen if we draw a 200x200 level-3 fractal rectangle whose initial color is green? Check your answer experimentally.

i. Change the computation of the intermediate boundaries so that midcol1 is 1/4 of the way across, midcol2 is 1/2 of the way across, midrow1 is 1/4 of the way down, and midrow2 is 1/2 of the way down.

j. What do you expect to have happen if we draw a 200x200 level-2 fractal rectangle whose initial color is red? Check your answer experimentally. What about a level-3 fractal?

k. Change the computation of the intermediate boundaries so that midcol1 is 1/4 of the way across, midcol2 is 3/4 of the way across, midrow1 is 1/4 of the way down, and midrow2 is 3/4 of the way down.

l. Draw one final level-3 fractal.

Assignment

Okay, you're finally done with the preparation. On to the assignment.

A. Using the basic ideas presented above, rewrite fractal-rectangle! to make the most interesting image that you can. You might change the size of the subrectangles, the function used to compute their colors, or even the number of subrectangles.

B. In addition, write a procedure, random-fractal-rectangle!, that behaves much like fractal-rectangle! except that at least one of the computations (e.g., of the columns, rows, and/or colors) involves a random number.

You may have noticed that level-3 fractals can take quite a while to draw. In your experiments, you may want to try your code with recursion levels of 1 and 2 to see what it looks like before trying recursion level 3.

Important Evaluation Criteria

We will judge your procedures both on the quality of the images they produce and on the cleverness of your modifications to the code.

Creative Commons License

Samuel A. Rebelsky, rebelsky@grinnell.edu

Copyright 2007 Janet Davis, Matthew Kluber, and Samuel A. Rebelsky. (Selected materials copyright by John David Stone and Henry Walker and used by permission.)

This material is based upon work partially supported by the National Science Foundation under Grant No. CCLI-0633090. Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.

This work is licensed under a Creative Commons Attribution-NonCommercial 2.5 License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/2.5/ or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA.