Skip to main content

>

Project Ideas: Fractals

Color trees provide one way of decomposing and then rebuilding an image. But, at least as created using the techniques in the companion reading, they create unpredictable images. As we explore in this section, one can more systematically build an image by decomposing it into smaller pieces.

A mathematically interesting kind 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 or 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
;;;   right, the right edge of the rectangle
;;;   bottom, the bottom edge 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
      ; Base case: We're at a level in which we just draw the rectangle.
      [(= level 0)
       (context-set-fgcolor! color)
       (image-select-rectangle! image REPLACE
                                left top 
                                (- right left)
                                (- bottom top))
       (image-fill-selection! image)
       (image-select-nothing! image)
       (context-update-displays!)]
      ; Recursive case: Break the rectangle into a few parts and recur
      ; on each.
      [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.

What can we do? Well, instead of drawing all sub-rectangles the same way, we can vary them a bit. For example, we might draw some of the sub-rectangles in the complement of the color, in a lighter version of the color, or in a darker version of the color. We might break up the rectangle into a less even grid. We might use different levels of recursion for different sub-rectangles. We will explore these kinds of options in the corresponding lab.