Functional Problem Solving (CSC 151 2014F) : Labs

Laboratory: Project Ideas


Summary: In this laboratory, you will explore the three new approaches to making images that were covered in the corresponding reading.

Preparation

Make a copy of project-ideas-lab.rkt, which contains much of the code from the reading.

Exercises on Color Trees

Exercise 1: Color Tree Basics

a. What effect do you expect the following instructions to have?

> (define t1 (cons "grey" (cons "red" "black")))
> (define image1 (ctree->image t1 200 200))
> (image-show image1)

b. Check your answer experimentally.

c. What effect do you expect the following instructions to have?

> (define t2 (cons (cons "red" t1) t1))
> (define image2 (ctree->image t2 200 200))
> (image-show image2)

d. Check your answer experimentally.

e. What effect do you expect the followings instruction to have?

> (define image3 (image-new 200 200))
> (image-render-color-tree! image3 t1 0 0 100 100)
> (image-render-color-tree! image3 t1 100 100 50 25)
> (image-render-color-tree! image3 t1 100 150 25 25)
> (image-show image3)

f. Check your answer experimentally.

g. Create one or two color trees of your own and render them.

Exercise 2: New Ways to Render Color Trees

a. The base case for image-render-color-tree! is to render the area as a rectangle. What do you expect to happen if we change the call to image-select-rectangle! to image-select-ellipse!?

b. Check your answer experimentally. Then restore the call to image-select-rectangle!.

c. As written, the width used in recursive calls during horizontal splits is (/ width 2). What do you expect to have happen if we change that to (/ width 2.5)? (Assume we change it for both recursive calls.)

d. Check your answer experimentally. Then restore the original formula.

e. As written, the top edge used in recursive calls for vertical splits is (+ top (/ height 2)). What do you expect to have happened if we use (+ top (/ height 1.5))?

f. Check your answer experimentally. Then restore the original formula.

Exercise 3: A Simple Series of Images

Look at the code for series-1.

a. What do you expect (series-1 5 100 100) to produce?

b. Check your answer experimentally.

c. What do you expect (series-1 42 100 100) to produce?

d. Check your answer experimentally.

e. Do you expect (series-1 43 100 100) to be similar to or very different from the image you got using 42? How do you expect it to differ?

f. Check your answer experimentally.

g. How do you expect (series-1 43 500 500) to differ?

h. Check your answer experimentally.

i. How do you expect (series-1 43 800 100) to relate to the previous two images?

j. Check your answer experimentally.

Exercises on Fractals

Exercise 4: Simple Fractal Activities

a. Use fractal-rectangle! to draw a 200x200 blue rectangle on canvas, using a recursion level of 0.

b. 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. 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. Here's a modified version of fractal-rectangle! in which we've changed the recursive calls for the top-middle, left-middle, right-middle, and bottom-middle subrectangles so that they use the complement of the color.

(define fractal-rectangle-c!
  (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-c! image 
                               color
                               left top 
                               midcol1 midrow1
                               (- level 1))
         (fractal-rectangle-c! image 
                               (rgb-complement color)
                               midcol1 top 
                               midcol2 midrow1
                               (- level 1))
         (fractal-rectangle-c! image 
                               color
                               midcol2 top 
                               right midrow1
                               (- level 1))
         ; Second row of squares
         (fractal-rectangle-c! image 
                               (rgb-complement color)
                               left midrow1
                               midcol1 midrow2
                               (- level 1))
         (fractal-rectangle-c! image 
                               color
                               midcol1 midrow1
                               midcol2 midrow2
                               (- level 1))
         (fractal-rectangle-c! image 
                               (rgb-complement color)
                               midcol2 midrow1
                               right midrow2
                               (- level 1))
         ; Third row of squares
         (fractal-rectangle-c! image 
                               color
                               left midrow2
                               midcol1 bottom
                               (- level 1))
         (fractal-rectangle-c! image 
                               (rgb-complement color)
                               midcol1 midrow2
                               midcol2 bottom
                               (- level 1))
         (fractal-rectangle-c! image 
                               color
                               midcol2 midrow2
                               right bottom
                               (- level 1))
         )))))

Put this new version at the end of your definitions pane.

Use this procedure to 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. Then check your prediction experimentally.

f. Here is a procedure that averages two RGB colors.

;;; Procedure:
;;;   rgb-average
;;; Parameters:
;;;   rgb1, an RGB color
;;;   rgb2, an RGB color
;;; Purpose:
;;;   Compute the "average" of two RGB colors.  
;;; Produces:
;;;   rgb-avg, an RGB color
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   (rgb-red rgb-avg) is the average of (rgb-red rgb1) and (rgb-red rgb2)
;;;   (rgb-green rgb-avg) is the average of (rgb-green rgb1) and (rgb-green rgb2)
;;;   (rgb-blue rgb-avg) is the average of (rgb-blue rgb1) and (rgb-blue rgb2)
(define rgb-average
  (lambda (rgb1 rgb2)
    (rgb-new (quotient (+ (rgb-red rgb1) (rgb-red rgb2)) 2)
             (quotient (+ (rgb-green rgb1) (rgb-green rgb2)) 2)
             (quotient (+ (rgb-blue rgb1) (rgb-blue rgb2)) 2))))

And here is a rewritten fractal-rectangle! that averages the colors in the top-middle, left-middle, right-middle, and bottom-middle subrectangles with black and the colors in the other five subrectangles with white.

(define fractal-rectangle-a!
  (let ([RGB-WHITE (irgb 255 255 255)]
        [RGB-BLACK (irgb 0 0 0)])
    (lambda (image color left top right bottom level)
      (cond
       ; Base case: We're at a level in which we just draw the rectangle-a.
       [(= 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-a 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-a! image 
                                (rgb-average color RGB-WHITE)
                                left top 
                                midcol1 midrow1
                                (- level 1))
          (fractal-rectangle-a! image 
                                (rgb-average color RGB-BLACK)
                                midcol1 top 
                                midcol2 midrow1
                                (- level 1))
          (fractal-rectangle-a! image 
                                (rgb-average color RGB-WHITE)
                                midcol2 top 
                                right midrow1
                                (- level 1))
          ; Second row of squares
          (fractal-rectangle-a! image 
                                (rgb-average color RGB-BLACK)
                                left midrow1
                                midcol1 midrow2
                                (- level 1))
          (fractal-rectangle-a! image 
                                (rgb-average color RGB-WHITE)
                                midcol1 midrow1
                                midcol2 midrow2
                                (- level 1))
          (fractal-rectangle-a! image 
                                (rgb-average color RGB-BLACK)
                                midcol2 midrow1
                                right midrow2
                                (- level 1))
          ; Third row of squares
          (fractal-rectangle-a! image 
                                (rgb-average color RGB-WHITE)
                                left midrow2
                                midcol1 bottom
                                (- level 1))
          (fractal-rectangle-a! image 
                                (rgb-average color RGB-BLACK)
                                midcol1 midrow2
                                midcol2 bottom
                                (- level 1))
          (fractal-rectangle-a! image 
                                (rgb-average color RGB-WHITE)
                                midcol2 midrow2
                                right bottom
                                (- level 1))
          )]))))

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. Let's 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. Add the following to the end of your definitions pane.

(define fractal-rectangle-u!
  (let ([RGB-WHITE (irgb 255 255 255)]
        [RGB-BLACK (irgb 0 0 0)])
    (lambda (image color left top right bottom level)
      (cond
       ; Base case: We're at a level in which we just draw the rectangle-u.
       ((= 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-u into a few parts and recur
       ; on each.
       (else
        (let* ([midcol1 (round (+ left (/ (- right left) 4)))]
              [midcol2 (round (+ midcol1 (/ (- right left) 4)))]
              [midrow1 (round (+ top (/ (- bottom top) 4)))]
              [midrow2 (round (+ midrow1 (/ (- bottom top) 4)))])
          ; First row of squares
          (fractal-rectangle-u! image 
                                (rgb-average color RGB-WHITE)
                                left top 
                                midcol1 midrow1
                                (- level 1))
          (fractal-rectangle-u! image 
                                (rgb-average color RGB-BLACK)
                                midcol1 top 
                                midcol2 midrow1
                                (- level 1))
          (fractal-rectangle-u! image 
                                (rgb-average color RGB-WHITE)
                                midcol2 top 
                                right midrow1
                                (- level 1))
          ; Second row of squares
          (fractal-rectangle-u! image 
                                (rgb-average color RGB-BLACK)
                                left midrow1
                                midcol1 midrow2
                                (- level 1))
          (fractal-rectangle-u! image 
                                (rgb-average color RGB-WHITE)
                                midcol1 midrow1
                                midcol2 midrow2
                                (- level 1))
          (fractal-rectangle-u! image 
                                (rgb-average color RGB-BLACK)
                                midcol2 midrow1
                                right midrow2
                                (- level 1))
          ; Third row of squares
          (fractal-rectangle-u! image 
                                (rgb-average color RGB-WHITE)
                                left midrow2
                                midcol1 bottom
                                (- level 1))
          (fractal-rectangle-u! image 
                                (rgb-average color RGB-BLACK)
                                midcol1 midrow2
                                midcol2 bottom
                                (- level 1))
          (fractal-rectangle-u! image 
                                (rgb-average color RGB-WHITE)
                                midcol2 midrow2
                                right bottom
                                (- level 1))
          ))))))

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.

Exercise 5: Varying Parameters

Sometimes it is difficult to envision how you make 1000 variations of an image. But, as is often the case, the problem is a bit easier if you break it down into parts. Suppose we want all of our images to be based on the “spin a polygon” technique. In building different images, we can vary

  • the number of sides (that is, the type of polygon);
  • the angle at which the turtle is drawn;
  • the number of copies we draw; and
  • the color we use to draw the polygons.

(We probably can't vary the side length, since that should depend on the size of the image.)

Now, how do we turn one number (the n we're using to describe the image) into four different numbers (or six, if we change the red, green, and blue components separately)? We can use modulo

(define series-2 
  (lambda (n width height)
    (let* ((canvas (image-new width height))
           (width (exact->inexact width))
	   (height (exact->inexact height))
           (t (turtle-new canvas))
           (side-length (/ (+ width height) 12))
           (sides (+ 3 (modulo n 4)))
           (angle (* 5 (modulo n 5)))
           (copies (+ 5 (* 2 (modulo n 7))))
           (red (* 256/11 (modulo n 11)))
           (green (* 256/13 (modulo n 13)))
           (blue (- 256 red)))
      (turtle-set-color! t (rgb-new red green blue))
      (turtle-teleport! t (/ width 3) (/ height 3))
      (turtle-spin-polygon! t side-length sides angle copies)
      (image-show canvas)
      canvas)))

a. Try this procedure with a variety of values for n, width, and height

b. In your own words, explain how the procedure works to vary the drawing.

c. Can we be sure that this procedure creates 1000 different drawings for the values of n from 0 to 999? Why or why not?