2011-10-29

SICP Exercise 2.52: Abusing Painters

Make changes to the square limit of wave shown in figure 2.9 by working at each of the levels described above. In particular:
  1. Add some segments to the primitive wave painter of exercise 2.49 (to add a smile, for example).
  2. Change the pattern constructed by corner-split (for example, by using only one copy of the up-split and right-split images instead of two).
  3. Modify the version of square-limit that uses square-of-four so as to assemble the corners in a different pattern. (For example, you might make the big Mr. Rogers look outward from each corner of the square.)
Before we get started, let's see what our current procedures give us. We're going to be modifying the wave painter, so we'll show what that looks like with square-limit. If we try ((square-limit wave 4) window) we get:
Now let's add a smile and a pair of eyes to the wave painter. I quickly sketched out what I wanted to add as follows:
From this we can work out the coordinates for the eyes and smile and then add in another three sets of connected segments using build-segments-list as we did in exercise 2.49. The extended wave painter looks like this, with the new connected segment sets added at the bottom:
(define wave
 (segments->painter
   (append (build-segments-list (make-vect 0.0  0.85)
                                (make-vect 0.15 0.6)
                                (make-vect 0.3  0.65)
                                (make-vect 0.4  0.65)
                                (make-vect 0.35 0.85)
                                (make-vect 0.4  1.0))
           (build-segments-list (make-vect 0.6  1.0)
                                (make-vect 0.65 0.85)
                                (make-vect 0.6  0.65)
                                (make-vect 0.75 0.65)
                                (make-vect 1.0  0.35))
           (build-segments-list (make-vect 1.0  0.15)
                                (make-vect 0.6  0.45)
                                (make-vect 0.75 0.0))
           (build-segments-list (make-vect 0.6  0.0)
                                (make-vect 0.5  0.3)
                                (make-vect 0.4  0.0))
           (build-segments-list (make-vect 0.25 0.0)
                                (make-vect 0.35 0.5)
                                (make-vect 0.3  0.6)
                                (make-vect 0.15 0.4)
                                (make-vect 0.0  0.65))
           (build-segments-list (make-vect 0.4 0.9)
                                (make-vect 0.45 0.9)
                                (make-vect 0.45 0.85)
                                (make-vect 0.4 0.85)
                                (make-vect 0.4 0.9))
           (build-segments-list (make-vect 0.55 0.9)
                                (make-vect 0.6 0.9)
                                (make-vect 0.6 0.85)
                                (make-vect 0.55 0.85)
                                (make-vect 0.55 0.9))
           (build-segments-list (make-vect 0.4 0.75)
                                (make-vect 0.45 0.7)
                                (make-vect 0.55 0.7)
                                (make-vect 0.6 0.75)))))
Trying this out with (wave window) gives:
...and ((square-limit wave 4) window) gives:
Next we can work on corner-split. The exercise suggests only using one copy of the up-split and right-split images, instead of two. This is a nice straightforward change. Here's the original corner-split:
(define (corner-split painter n)
  (if (= n 0)
      painter
      (let ((up (up-split painter (- n 1)))
            (right (right-split painter (- n 1))))
        (let ((top-left (beside up up))
              (bottom-right (below right right))
              (corner (corner-split painter (- n 1))))
          (beside (below painter top-left)
                  (below bottom-right corner))))))
As you can see it defines top-left as applying beside with up as both parameter values and bottom-right as applying below with right as both parameter values. We can see the effect of corner-split by applying it to the updated wave via ((corner-split wave 4) window):
All we need to do is to remove these definitions and replace usages of top-left and bottom-right with up and right respectively. If we do this (and collapse the let statements as the nesting is no longer needed) we get:
(define (corner-split painter n)
  (if (= n 0)
      painter
      (let ((up (up-split painter (- n 1)))
            (right (right-split painter (- n 1)))
            (corner (corner-split painter (- n 1))))
        (beside (below painter up)
                (below right corner)))))
Testing this with ((corner-split wave 4) window) now gives:
...and ((square-limit wave 4) window) now gives:
Finally we can look at changing square-limit. The exercise suggests making "the big Mr. Rogers look outward from each corner of the square". If we look at the quadrants as they're shown in section 2.2.4 we can see that Mr. Rodgers currently looks inward at the center of the square. E.g. in the top-right quadrant Mr. Rodgers' face is in the bottom-left-hand corner and looks towards the left.

Now there are at least a couple of ways in which you could interpret this suggested change depending upon what orientation of the head you end up with in each quadrant. I decided to go with the simplest - I'll have Mr. Rodgers' face have the correct orientation in the bottom-right quadrant. I.e. in the bottom-right quadrant Mr. Rodgers' face will be in the bottom-left-hand corner and look towards the left. This then reduces the problem to simply swapping over the contents of opposite corners of the square. So we take the original square-limit of:
(define (square-limit painter n)
  (let ((combine4 (square-of-four flip-horiz identity
                                  rotate180 flip-vert)))
    (combine4 (corner-split painter n))))
...and then swap the top-left corner with the bottom-right and the top-right corner with the bottom-left to give:
(define (square-limit painter n)
  (let ((combine4 (square-of-four flip-vert rotate180
                                  identity flip-horiz)))
    (combine4 (corner-split painter n))))
When we try this out with ((square-limit wave 4) window) we now get:

No comments:

Post a Comment