6.001, Spring Semester, 1997--Problem Set 4

picture503

MASSACHVSETTS INSTITVTE OF TECHNOLOGY
Department of Electrical Engineering and Computer Science
6.001--Structure and Interpretation of Computer Programs
Spring Semester, 1997

Problem Set 4

A Graphics Design Language

Issued: Tuesday, February 25, 1997

Due: In recitation on Wednesday, March 5 for recitations held at 9, 12, 1 and 2;
and on Friday, March 7 for recitations held at 10 and 11.

Tutorial Preparation: For the week of March 3.

Reading Assignment: Through the end of Section 2.3 of SICP.

In this assignment, you will work with Peter Henderson's ``square-limit'' graphics design language, which will be described in lecture on February 27, and which appears in the text. Before beginning work on this programming assignment, you should review that section. The goal of this problem set is to reinforce ideas about data abstraction and higher-order procedures, and to emphasize the expressive power that derives from appropriate primitives, means of combination, and means of abstraction.

Figure belongs here but could not convert from postscript

Section 1 of this handout reviews the language, similar to what appears in the text. You will need to study this in order to prepare the tutorial presentations in section 2. Section 3 gives the lab assignment, which includes an optional design contest.

1. The Square-limit language

Recall that the key idea in the square-limit language is to use painter procedures that take frames as inputs and paint images that are scaled to fit the frames. To do this, we will need some basic building blocks.

Basic data structures

Each vector consists of a pair of numbers, glued together in some appropriate manner. The contract we enforce for this data abstraction is the following:

We need a set of operations on vectors, and in particular assume three:

A pair of vectors determines a directed line segment--the segment running from the endpoint of the first vector to the endpoint of the second vector. Again, we just need a contract:

Frames

A frame is represented by three vectors: an origin and two edge vectors.

Thus, make-frame is of type: tex2html_wrap_inline1310 . Each selector is of type: tex2html_wrap_inline1312 .

The frame's origin is given as a vector with respect to the origin of the graphics-window coordinate system. The edge vectors specify the offsets of the corners of the frame from the origin of the frame. If the edges are perpendicular, the frame will be a rectangle; otherwise it will be a more general parallelogram. Figure 2 shows a frame and its associated vectors.

   figure208
Figure: A frame is described by three vectors--an origin and two edges.

Each frame determines a system of ``frame coordinates'' (x,y) where (0,0) is the origin of the frame, x represents the displacement along the first edge (as a fraction of the length of the edge) and y is the displacement along the second edge. For example, the origin of the frame has frame coordinates (0,0) and the vertex diagonally opposite the origin has frame coordinates (1,1).

Another way to express this idea is to say that there is a frame coordinate map associated with each frame that takes vectors expressed in terms of the coordinate system defined by that frame, and returns the same vectors, expressed in terms of the original or underlying coordinate system. That is, (x,y) gets mapped onto the underlying coordinates of the point given by the vector sum

displaymath214

We can obtain a procedure which computes the frame coordinate map of a frame by applying make-frame-coordinate-map of type tex2html_wrap_inline1328 (which means it takes a frame as argument and returns a procedure which maps vectors to vectors):

For example, ((make-frame-coordinate-map a-frame) (make-vect 0 0)) will return the same value as (origin-frame a-frame).

The procedure make-relative-frame provides a convenient way to transform frames. Given three points origin, corner1, and corner2 (expressed in frame coordinates), it returns a procedure which for any frame f returns a new frame g which uses those points in f coordinates to define the corners of the g frame (this procedure thus has type tex2html_wrap_inline1340 ):

For example,

returns a procedure that takes a frame f and returns the upper right quarter of f as a new frame g.

Painters

As described in the notes, a painter is a procedure that, given a frame as argument, ``paints'' a picture in the frame. That is to say, if p is a painter and f is a frame, then evaluating (p f) will cause an image to appear in the frame. The image will be scaled and stretched to fit the frame. Denoting the type of this procedure is a bit awkward since the result of using a painter is to cause a picture to appear, rather than returning a specific object. For notational convenience, we will say that a painter is of type tex2html_wrap_inline1354 by which we mean that a painter takes a frame as input, and returns a set of points, represented as vectors, that are actually drawn in the frame.

The language you will be working with includes four ways to create primitive painters.

The simplest painters are created with number->painter, which takes a number as argument. These painters fill a frame with a solid shade of gray. The number specifies a gray level: 0 is black, 255 is white, and numbers in between are increasingly lighter shades of gray. Here are some examples:

You can also specify a painter using procedure->painter, which takes a procedure as argument. The procedure determines a gray level (0 to 255) as a function of (x,y) position, for example:

The x and y coordinates run from 0 to 1 and specify the fraction that each point is offset from the frame's origin along the frame's edges. Thus, the frame is filled out by the set of points (x,y) such that tex2html_wrap_inline1368 .

A third kind of painter is created by segments->painter, which takes a list of line segments as argument. This paints the line drawing specified by the list segments. The (x,y) coordinates of the line segments are specified as above. For example, you can make the ``Z'' shape shown in figure 3 as

The final way to create a primitive painter is from a stored image. The procedure image->painter uses an image from the 6001 image collection to create a painter gif. For instance:

    (define fovnder (image->painter "fovnder"))

will paint an image of William Barton Rogers, the FOVNDER of MIT.

Figure here unable to convert from postscript
Examples of primitive painters: mark-of-zorro and fovnder.

Transforming and combining painters

Given a painter, we can produce a new painter that transforms its frame before painting in it. For example, if p is a painter and f is a frame, then

will paint in the upper-right-hand corner of the frame.

We can abstract this idea with the following procedure:

    (define (make-painter-transformer origin corner1 corner2) (lambda (painter) (compose painter (make-relative-frame origin corner1 corner2))))

This procedure is of type tex2html_wrap_inline1386 . Note how the type helps us understand the ways in which make-painter-transformer can be used, i.e., its returned value must take a painter as input and will return a painter, which means by closure that the result can be again subjected to any other transformer.

Calling make-painter-transformer with an origin and two corners returns a procedure that transforms a painter into one that paints relative to a new frame with the specified origin and corners. Note how it accomplishes this - it uses make-relative-frame to return a procedure that takes a frame as argument and returns a new frame. Thus when the procedure returned by make-painter-transformer is applied to a painter, the expression involving compose first applies this new frame procedure to the initial frame, and creates a new frame to which the painter is then applied. For example, we could define:

    (define (shrink-to-upper-right painter) ((make-painter-transformer (make-vect .5 .5) (make-vect 1 .5) (make-vect .5 1)) painter))

Another way to define shrink-to-upper-right is:

    (define shrink-to-upper-right (make-painter-transformer (make-vect .5 .5) (make-vect 1 .5) (make-vect .5 1)))

Another transformed frame, called flip-horiz should flip images horizontally (we'll ask you to implement this later), another rotates images counterclockwise by 90 degrees:

    (define rotate90 (make-painter-transformer (make-vect 1 0) (make-vect 1 1) (make-vect 0 0)))

and similar rotations, rotate180 and rotate270, can be created.

We can combine the results of two painters in a single frame by calling each painter on the frame:

    (define (superpose painter1 painter2) (lambda (frame) (begin (painter1 frame) (painter2 frame))))

Superpose is of type tex2html_wrap_inline1396 .

To draw one image beside another, we combine one in the left half of the frame with one in the right half of the frame:

    (define (beside painter1 painter2) (let ((split-point (make-vect 0.5 0.0))) (superpose ((make-painter-transformer (make-vect 0.0 0.0) split-point (make-vect 0.0 1.0)) painter1) ((make-painter-transformer split-point (make-vect 1.0 0.0) (make-vect 0.5 1.0)) painter2))))

We can also define painters that combine painters vertically, by using rotate together with beside. The painter produced by below shows the image for painter1 below the image for painter2:

    (define (below painter1 painter2) (rotate270 (beside (rotate90 painter2) (rotate90 painter1))))

2. Tutorial exercises

You should prepare the following exercises for oral presentation in tutorial. They cover material in sections 2.1 and 2.2 of the text, and they also test your understanding of the square-limit language described above, in preparation for doing this week's lab exercises. You may wish to use the computer to check your answers to these questions, but you should try to do them (at least initially) without the computer.

Tutorial exercise 1:

Draw box-and-pointer diagrams for the lists a and b defined as follows:

    (define a (list 2 3 5)) (define b (append a (reverse a)))

where append and reverse are built-in procedures that could be defined as follows:

    (define (append x y) (if (null? x) y (cons (car x) (append (cdr x) y))))

    (define (reverse x) (define (iter x y) (if (null? x) y (iter (cdr x) (cons (car x) y)))) (iter x '()))

Tutorial Exercise 2:

Demonstrate that make-painter-transformer is of the type stated above. What does

    (compose (make-painter-transformer (make-vect 0 0) (make-vect 0.5 0) (make-vect 0 1)) (make-painter-transformer (make-vect 0.5 0) (make-vect 1 0) (make-vect 0 1)))

do? Use types to verify your answer, and if needed use the substitution model to work things through.

Tutorial Exercise 3:

Do Exercise 2.29 from the text.

3. To do in lab

If you are going to try to do the problem set at home or on Athena, then be sure to read the Problem Set 4 Warning File on the 6.001 web site for special directions. Also be sure to leave yourself enough time so that you can still go to the lab and get your work done, if the Athena or PC version does not work.

Load the code for problem set 4, which contains the procedures described in section 1. You will not need to modify any of these. We suggest that you define your new procedures in a separate (initially empty) editor buffer, to make it easy to reload the system if things get fouled up.

When the problem set code is loaded, it will create three graphics windows, named g1, g2, and g3. To paint a picture in a window, use the procedure paint. Paint takes a graphics window and a painter, determines the frame for the graphics window, and gives the frame to the painter. For example - once you have successfully completed lab exercise 1 and successfully evaluated (load-rest) - then

    (paint g1 fovnder)

will show a picture of William Barton Rogers in window g1.

There is also a procedure called paint-hi-res, which paints the images at higher resolution ( tex2html_wrap_inline1412 rather than tex2html_wrap_inline1374 ). Painting at a higher resolution produces better looking images, but takes four times as long. As you work on this problem set, you should look at the images using paint, and reserve paint-hi-res to see the details of images that you find interesting.gif

Printing pictures

You can print images on the laserjet printer with Edwin's M-x print-graphics command as described in the Don't Panic manual. (PC users be careful - you will migh have to come to the lab to print out your pictures for the final write up - check the warning file on the web for details.) The laserjet cannot print true greyscale pictures, so the pictures will not look as good as they do on the screen. Please print only a few images--only the ones that you really want--so as not to waste paper and clog the printer queues. We suggest that you print only images created with paint-hi-res, not paint.

Lab exercise 1:

Complete the implementation of the data abstractions, by choosing a representation for vectors (make-vect, xcor-vect, ycor-vect) and for segments (make-segment, start-segment, end-segment). Use those abstractions to implement the three operations on vectors (add-vect, sub-vect, scale-vect). Once you are done adding your definitions, save the file and then load it into your Scheme buffer.

If you work on the problem set in multiple sessions, be sure that you reload this file after you have loaded up the problem set, so that your new definitions will override the ones in the problem set file. You will also have to redo the (load-rest) evaluation described below.

Turn in a listing of your code, and some examples of using it.

Once you have created your data abstractions, evaluate

    (load-rest)

to have the rest of the code for the problem set loaded into your Scheme environment.

Lab exercise 2:

Make a collection of primitive painters to use in the rest of this lab. In addition to the ones predefined for you (and listed in section 1), define at least one new painter of each of the four primitive types: a uniform grey level made with number->painter, something defined with procedure->painter, a line-drawing made with segments->painter, and an image of your choice that is loaded from the 6001 image collection with image->painter. Turn in a list of your definitions.

Lab exercise 3:

Earlier we referred to examples of transforming painters, i.e. procedures that take a painter as input and create a new painter that will draw relative to some new frame. Increase the repetoire of such methods by implementing a transformation, flip-horiz, which takes as input a painter, and returns a new painter that draws its input flipped about the horizontal axis. Also implement rotate180 and rotate270 in analogy to rotate90. Turn in a listing of your procedures.

Lab exercise 4:

Experiment with some combinations of your primitive painters, using beside, below, superpose, flips, and rotations, to get a feel for how these means of combination work. You needn't turn in anything for this exercise.

Lab exercise 5:

The ``diamond'' of a frame is defined to be the smaller frame created by joining the midpoints of the original frame's sides, as shown in figure 4. Define a procedure diamond that transforms a painter into one that paints its image in the diamond of the specified frame, as shown in figure 4. Try some examples, and turn in a listing of your procedure.

Figure unable to convert from postscript The ``diamond'' of a frame is formed by joining the midpoints of the sides. This is illustrated with a painting created by (diamond fovnder).

Lab exercise 6:

The ``diamond'' transformation has the property that, if you start with a square frame, the diamond frame is still square (although rotated). Define a transformation similar to diamond, but which does not produce square frames. Try your transformation on some images to get some nice effects. Turn in a listing of your procedure.

Lab exercise 7:

The following recursive right-split procedure was demonstrated in lecture:

    (define (right-split painter n) (if (= n 0) painter (let ((smaller (right-split painter (- n 1)))) (beside painter (below smaller smaller)))))

Try this with some of the painters you've previously defined, both primitives and combined ones. Now define an analogous up-split procedure as shown in figure 5. Make sure to test it on a variety of painters. Turn in a listing of your procedure. (In defining your procedure, remember that (below painter1 painter2) produces painter1 below painter2.)

   figure375
Figure 5: The up-split procedure places a picture below two (recursively) up-split copies of itself. This was created from (up-split fovnder 4)

Lab exercise 8:

Right-split and up-split are both examples of a common pattern that begins with a means of combining two painters and applies this over and over in a recursive pattern. We can capture this idea in a procedure called keep-combining, which takes as argument a combiner (which combines two painters). For instance, we should be able to give an alternative definition of right-split as

    (define new-right-split (keep-combining (lambda (p1 p2) (beside p1 (below p2 p2)))))

Complete the following definition of keep-combining:

    (define (keep-combining combine-2) ;; combine-2 = (lambda (painter1 painter2) ...) (lambda (painter n) ((repeated tex2html_wrap_inline1430 fill in missing expression tex2html_wrap_inline1432 n) painter)))

where repeated is given by:

    (define (repeated f n) (if (zero? n) identity (compose f (repeated f (- n 1)))))

Show that you can indeed define right-split using your procedure, and give an analogous definition of up-split.

Lab exercise 9

Once you have keep-combining, you can use it to define lots of recursive means of combination. Figure 6 shows an example, which comes from:

    (define nest-diamonds (keep-combining (lambda (p1 p2) (superpose p1 (diamond p2)))))

    (nest-diamonds fovnder 4)

Invent some variations of your own. Turn in the code and one or two sample pictures.

Figure unable to convert Some recursive combination schemes, defined with keep-combining.

Lab exercise 10:

The procedures you have implemented give you a wide choice of things to experiment with. Invent some new means of combination, both simple ones like beside and complex higher-order ones like keep-combining and see what kinds of interesting images you can create. Turn in the code and one or two figures.

Contest (Optional)

Hopefully, you generated some interesting designs in doing this assignment. If you wish, you can enter printouts of your best designs in the 6.001 PS4 design contest. Turn in your design collection together with your homework, but stapled separately, and make sure your name is on the work. For each design, show the expression you used to generate it. Designs will be judged by the 6.001 staff and other internationally famous art critics, and fabulous prizes will be awarded in lecture. There is a limit of two entries per student. Make sure to turn in not only the pictures, but also the procedure(s) that generated them.

How much time did you spend on this problem set?

If you cooperated with other students working this problem set please indicate their names on your solutions. As you should know, as long as the guidelines described in the 6.001 Policy on Cooperation handout are followed, such cooperation is allowed and encouraged.

About this document ...

This document was generated using the LaTeX2HTML translator Version 96.1-f (May 31, 1996) Copyright © 1993, 1994, 1995, 1996, Nikos Drakos, Computer Based Learning Unit, University of Leeds.

The command line arguments were:
latex2html -split 0 ps4hend.tex.

The translation was initiated by Jeremy Daniel on Tue Feb 25 04:50:03 EST 1997

...painter
The images are kept in the directory specified by the variable 6001-image-directory (which is 6001-images in the lab). The image->painter procedure transforms them into painters, so that they can be scaled and deformed by the operations in the square-limit language. Use the Edwin command M-x list-directory to see the entire contents of the directory. Each image is 15#2, stored in ``pgm'' format.
...interesting.
Painting a primitive image like fovnder won't look any different at high resolution, because the original picture is only 15#2. But as you start stretching and shrinking the image, you will see differences at higher resolution.
 


Jeremy Daniel
Tue Feb 25 04:50:03 EST 1997