Subject: PS3 solutions
From: jmiller@crl.dec.com
























Exercise 2.1:

      ---------   ---------   ---------
      |   |   |   |   |   |   |   |  /|
a ->  | 2 | --|-> | 3 | --|-> | 5 | / |
      |   |   |   |   |   |   |   |/  |
      ---------   ---------   ---------


      ---------   ---------   ---------   ---------   ---------   ---------
      |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |  /|
b ->  | 2 | --|-> | 3 | --|-> | 5 | --|-> | 5 | --|-> | 3 | --|-> | 2 | / |
      |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |/  |
      ---------   ---------   ---------   ---------   ---------   ---------

Exercise 2.2:

(procedure->painter (lambda (x y) (* x y)))
  Basically all black with only a slight variation (because the gray
  level only varies from 0 at the origin (lower left) to 1 at the
  upper left cormer).

(procedure->painter (lambda (x y) (* 255 x y)))
  A continuously varying image from black at the bottom and left edges
  to white at the top, right point.



























Exercise 2.3:

Here's a picture:

  (frame-edge2 frame)
	    ^            |
	    |    P2      |
	    |            |
	    *------------|   * is the new origin of P2 and the
	   ^|            |     new edge 2 for P1
	  / |    P1      |
 origin  /  |            |
 of P2	/   --------------> (frame-edge1 frame)
       /   ^
      /   /
     /   / (frame-origin frame)
     |	/  also origin of P1
     | /
     |/
    (0,0)

Notice that the origin for P1 is the same as the original origin; its
edge 1 is the original edge 1; and its edge 2 is half of the original
edge 2.

Similarly, the origin for P2 is the original origin plus half of the
original edge 2; its edge 1 is the original edge 1; and its edge 2 is
half of the original edge 2.


(define (below p1 p2)
  (lambda (frame)
    (p1 (make-frame
	 (frame-origin frame)
	 (frame-edge1 frame)
	 (vector-scale .5 (frame-edge2 frame))))
    (p2 (make-frame
	 (vector-add (frame-origin frame)
		     (vector-scale .5 (frame-edge2 frame)))
	 (frame-edge1 frame)
	 (vector-scale .5 (frame-edge2 frame))))))

Part 4.2

I wrote this code directly from the diagram in Figure 8.

(a) The new origin is side 1 of the original triangle, but with the
    origin shifted from the old origin to the screen coordinate system
    (the one with origin 0,0):
        (vector-shift (side1 triangle) old-origin zero-vector)
(b) The three arguments to make-triangle are
    - its origin in screen coordinates: new-origin
    - its side 1 relative to its origin, which is just side 2 of the
      original triangle shifted from the old to the new origin:
          (vector-shift (side2 triangle) old-origin new-origin)
    - its side 2 relative to its origin, which is just the origin of
      the original triangle shifted from the old to the new origin.
      Notice that the old origin in the old coordinate system is just
      the zero-vector:
          (vector-shift zero-vector old-origin new-origin)

(define (rotate pict)
  (define (rotated triangle)
    (let ((old-origin (triangle-origin triangle)))
      (let ((new-origin
	     (vector-shift (triangle-side1 triangle) old-origin zero-vector)))
      (pict (make-triangle
	     new-origin
	     (vector-shift (triangle-side2 triangle) old-origin new-origin)
	     (vector-shift zero-vector old-origin new-origin))))))
  rotated)

Part 4.3

All we have to do is draw the drawing in the specified triangle, draw
the rotated drawing in the same triangle, and draw the rotated rotated
drawing in the same triangle:

(define (three-fold drawing)
  (define (tripled triangle)
    (drawing triangle)
    ((rotate drawing) triangle)
    ((rotate (rotate drawing)) triangle))
  tripled)

Part 4.4

Here's how I think about the problem.  I would follow the hint and
compute the new origin, O:

  (vector-add (triangle-origin triangle)
	      (vector-add (vector-scale r (triangle-side1 triangle))
			  (vector-scale s (triangle-side2 triangle))))

Then the triangle in which P1 should be drawn has its origin at O and
its sides are the old origin (the zero-vector in the old coordinate
system) and the old side 1.  P2's triangle has its origin at O and its
sides are the old side 1 and side 2.  And P3's triangle has its origin
at O and its sides are the old side 2 and the original origin.

To translate it to code, I noticed that I need to use the old origin
and sides several times, and I need to use these same points in the
new coordinates several times.  So, I use LET to give names to all of
these 's the code:

(define (3split p1 p2 p3 r s)
  (define (splitter triangle)
    (let ((old-origin (triangle-origin triangle))
	  (old-side1 (triangle-side1 triangle))
	  (old-side2 (triangle-side2 triangle)))
      (let ((O (vector-add old-origin
			   (vector-add (vector-scale r old-side1)
				       (vector-scale s old-side2)))))
	(let ((new-origin (vector-shift zero-vector old-origin O))
	      (new-side1 (vector-shift old-side1 old-origin O))
	      (new-side2 (vector-shift old-side2 old-origin O)))
	  (P1 (make-triangle O new-origin new-side1))
	  (P2 (make-triangle O new-side1 new-side2))
	  (P3 (make-triangle O new-side2 new-origin))))))
  splitter)












Part 4.5

make-splitter is very much like 3split, but it takes the arguments at
a different time.  This is a common pattern in Scheme, and is easily
expressed:

(define (make-splitter r s)
  (lambda (p)
    (3split p p p r s)))

To test it, I tried the following:

  (define equal-split (make-splitter (/ 1 3) (/ 1 3)))
  (draw g2 (equal-split (superimpose outline-drawing v-shape)))

Part 4.6

Repeating the same idea as in 4.5, I get:

(define (make-rec-splitter r s)
  (define (split pict levels)
    (if (= levels 0)
	pict
	(let ((p (split pict (-1+ levels))))
	  (3split p p p r s))))
  split)

Part 4.7

This is somewhat more difficult, because the triangles don't have a
common origin.  Still, we can define the points q, r, and s in the old
coordinate system and then create triangles for each of the pictures.

(define (4split p1 p2 p3 p4 q r s)
  (lambda (triangle)
    (let ((orig (triangle-origin triangle))
	  (s1 (triangle-side1 triangle))
	  (s2 (triangle-side2 triangle)))
      (let ((s1-absolute (vector-shift s1 orig zero-vector))
	    (s2-absolute (vector-shift s2 orig zero-vector)))
	(let ((q-pt (vector-scale q s1))
	      (r-pt (vector-scale r s2))
	      (s-pt (vector-add (vector-scale s s1)
				(vector-scale (- 1 s) s2))))
	  (let ((q-absolute (vector-shift q-pt orig zero-vector)))
	    (p1 (make-triangle s1-absolute
			       (vector-shift q-pt orig s1-absolute)
			       (vector-shift s-pt orig s1-absolute)))
	    (p2 (make-triangle orig q-pt r-pt))
	    (p3 (make-triangle q-absolute
			       (vector-shift s-pt orig q-absolute)
			       (vector-shift r-pt orig q-absolute)))
	    (p4 (make-triangle s2-absolute
			       (vector-shift r-pt orig s2-absolute)
			       (vector-shift s-pt orig
					     s2-absolute)))))))))

(define (make-4-splitter q r s)
  (lambda (p) (4split p p p p q r s)))

(define (make-rec-4-splitter q r s)
  (define (splitter pict levels)
    (if (= levels 0)
	pict
	(let ((p (splitter pict (-1+ levels))))
	  (4split p p p p q r s))))
  splitter)

Part 4.9

The only two other operations on vectors are the two selectors,
vector-xcor and vector-ycor.  Everything else is written in terms of
these, so we only need to make the following changes:

(define vector-xcor car)   ;; Unchanged
(define vector-ycor cadr)  ;; Was CDR, not CADR
