2011-10-23

SICP Exercise 2.51: Below and Below Again

Define the below operation for painters. Below takes two painters as arguments. The resulting painter, given a frame, draws with the first painter in the bottom of the frame and with the second painter in the top. Define below in two different ways -- first by writing a procedure that is analogous to the beside procedure given above, and again in terms of beside and suitable rotation operations (from exercise 2.50).

Here's the procedure beside:
(define (beside painter1 painter2)
  (let ((split-point (make-vect 0.5 0.0)))
    (let ((paint-left
           (transform-painter painter1
                              (make-vect 0.0 0.0)
                              split-point
                              (make-vect 0.0 1.0)))
          (paint-right
           (transform-painter painter2
                              split-point
                              (make-vect 1.0 0.0)
                              (make-vect 0.5 1.0))))
      (lambda (frame)
        (paint-left frame)
        (paint-right frame)))))
This takes two painters and applies transformations to them such that the first painter is squashed into the left-hand side of a frame, while the second painter is squashed into the right-hand side of the frame... Assuming, of course, that the frame is orientated normally. I.e. with the origin at the bottom left-hand corner, edge1 defining the bottom side of the frame, going from left to right, and edge2 defining the left side of the frame, going from bottom to top. We can express this mapping graphically as follows:
We can use a similar pattern to this for our first version of below, except we want the mapping produced to divide the frame into two horizontal stripes, one above the other. Graphically this is:
To do this we need a different split-point, (0, 0.5) instead of (0.5, 0), and this split-point needs to split edge2 rather than edge1. And we need to rename a few variables. Other than that, it's pretty similar:
(define (below painter1 painter2)
  (let ((split-point (make-vect 0.0 0.5)))
    (let ((paint-down
           (transform-painter painter1
                              (make-vect 0.0 0.0)
                              (make-vect 1.0 0.0)
                              split-point))
          (paint-up
           (transform-painter painter2
                              split-point
                              (make-vect 1.0 0.5)
                              (make-vect 0.0 1.0))))
      (lambda (frame)
        (paint-down frame)
        (paint-up frame)))))
And putting this to the test with...
((below wave spiral) window)
...gives:
Now onto the second version... As we noted, beside puts its first painter on the left-hand side of the frame and its second on the right-hand side. We want below to put the first painter underneath the second painter. Graphically we can view this as the following rotation:
This rotation is a 90° anti-clockwise rotation. Fortunately, we've already been given a procedure for doing this, rotate90, which was introduced in the section "Transforming and combining painters". Unfortunately, as the image above shows, this translation also has the effect of rotating the contents of the painters by 90° anti-clockwise. We need to compensate for this by rotating each of the painters themselves by 90° clockwise, which is equivalent to rotating by 270° anti-clockwise and is encapsulated in the procedure rotate270 which we defined in exercise 2.50.

Here's what we get when we put these two sets of rotations together:
(define (below painter1 painter2)
  (rotate90 (beside (rotate270 painter1)
                    (rotate270 painter2))))
As you'd expect, this gives identical results to the first version above.

No comments:

Post a Comment