Assignment 3: Drawing Generally and Concisely


Due: 10:30 p.m. Tuesday, 16 September 2014

Summary: In this assignment, you will explore different ways to use the drawings-as-values model. In particular, you will create some procedures that vary existing drawings, strive to concisely describe some particular darwings, and write a procedure to draw an “interesting” group of pictures.

Purposes: To further practice with the drawings-as-values model. To consider concise algorithms for creating images with repetitive elements. To consider methods for scaling images to a given width and height. To gain experience writing procedures.

Collaboration: You must work with assigned partners on this assignment. The partner assignments are available at http://www.cs.grinnell.edu/~weinman/courses/CSC151/2014F/partners.html#2. You must collaborate on every problem - do not break up the work so that one person works on problem 1, another on problem 2, and another on problem 3. (The “don't break up the work” policy applies on every assignment. This note is just a reminder.)

Expected Time: Two to three hours. After two-and-a-half hours, stop what you're doing, write up what you've accomplished so far (which should take you less than half an hour) and write at the top of your assignment “I spent nearly three hours on this assignment. There's more to life than computer science!”. Doing so will earn you at least a check on this assignment if your group also visits with me in office hours to discuss the stumbling blocks you seem to have encountered.

Submitting: Email your answer to . The title of your email should have the form CSC-151-02 Assignment 3 and should contain your answers to all parts of the assignment. Scheme code should be in the body of the message. You should not attach any images; I should be able to re-create them from your code.

Warning: So that this assignment is a learning experience for everyone, we may spend class time publicly critiquing your work.

Preliminaries

You will need some of the code from the lab on procedures to test the examples given in this assignment.

Assignment

Problem 1: Adding Shadows

(a) Define a procedure that corresponds to the following documentation. Include this documentation along with your definition of the procedure.

;;; Procedure:
;;;   shadow
;;; Parameters:
;;;   drawing, a drawing
;;; Purpose:
;;;   Create a shadow for drawing.
;;; Produces:
;;;   my-shadow, a drawing
;;; Preconditions:
;;;   No additional.
;;; Postconditions:
;;;   my-shadow is the same size as drawing.  That is.
;;;     (drawing-width my-shadow) = (drawing-width drawing)
;;;     (drawing-height my-shadow) = (drawing-height drawing)
;;;   my-shadow is the same "shape" as drawing.  
;;;   my-shadow is colored grey.  
;;;   my-shadow is shifted up by 3 units and left by 6 units
;;;     relative to the position of drawing.  That is,
;;;     (drawing-left my-shadow) = (- (drawing-left drawing) 6)
;;;     (drawing-top my-shadow)  = (- (drawing-top drawing) 3)

Here are some examples of applying this procedure.

(shadow red-eye)

(shadow (hshift-drawing 10 blue-i))

(b) Define a procedure (pair-with-shadow drawing) that groups a drawing together with its shadow. The shadow should appear behind. For example,

(pair-with-shadow (hshift-drawing 20 (vshift-drawing 20 red-eye)))

(pair-with-shadow (hshift-drawing 20 (vshift-drawing 20 blue-i)))

(c) Document pair-with-shadow using the 6Ps.

(d) Write a procedure, (new-pair-with-shadow drawing color hoff voff), that behaves much like pair-with-shadow, except that one can specify the color of the shadow and the horizontal and vertical offset of the shadow.

Note: As in the case of pair-with-shadow, you may find it helpful to write a separate procedure to create the shadow.

Problem 2: Smaller Neighbors

The laboratory on writing your own procedures asks you to write a procedure, add-smaller-neighbor, that adds a slightly smaller neighbor to an image.

In solving this problem, most students wrote something like the following code.

(define add-smaller-neighbor
  (lambda (drawing)
    (drawing-group drawing
                   (hshift-drawing (drawing-width drawing)
                                   (scale-drawing .75 drawing)))))

This procedure works fine if the input drawing is on the left margin of the screen. Unfortunately, if the input drawing is not on the left margin of the screen, the smaller neighbor overlaps the input drawing, rather than being on the side.

(define thingy
 (hshift-drawing
  100
  (vshift-drawing
   60
   (scale-drawing
    80
    (drawing-group
     (recolor-drawing "black" drawing-unit-square)
     (recolor-drawing "red" drawing-unit-circle))))))
(image-show (drawing->image thingy 200 100))
(image-show (drawing->image (add-smaller-neighbor thingy) 200 100))

Why do we have this problem? Because drawing-scale scales everything - not just the shape (or shapes), but also the left and top offset.

How do we solve the problem? There are a variety of options. Here are two. You might choose to temporarily shift the drawing back to the left margin and then shift the pair back after making the neighbor. If that's too much effort, you can come up with a better formula for how much to shift the right neighbor. (The math isn't too hard.)

It will be easiest if we break the problem down into two parts: Making the right neighbor and adding it to the original drawing.

Once we know how to make a right neighbor, adding it is easy.

(define add-smaller-right-neighbor
  (lambda (drawing)
    (drawing-group drawing (smaller-right-neighbor drawing))))

Your goal is therefore to write smaller-right-neighbor. To help you in this endeavor, here's some documentation.

;;; Procedure:
;;;   smaller-right-neighbor
;;; Parameters:
;;;   drawing, a drawing
;;; Purpose:
;;;   Create a smaller version of drawing, situated immediately to the
;;;   right of drawing.
;;; Produces:
;;;   neighbor, a drawing
;;; Preconditions:
;;;   (drawing-width drawing) > 0
;;;   (drawing-height drawing) > 0
;;; Postconditions:
;;;   (drawing-width neighbor) = (* 0.75 (drawing-width drawing))
;;;   (drawing-height neighbor) = (* 0.75 (drawing-height drawing))
;;;   (drawing-top neighbor) = (drawing-top drawing)
;;;   (drawing-left neighbor) = (drawing-right drawing)
;;;   The colors and shapes of neighbor are essentially the same as
;;;     those of drawing.

(a) Implement smaller-right-neighbor. That is, define the procedure.

Make sure that you test your procedure on a variety of drawings, including some drawings that have their left edge to the left of the image (that is, less than zero) and that have their top edge at various places.

(b) What if we want the right neighbor to be something other than 75% of the original drawing? We might add a second parameter to specify the scale factor. Implement a procedure, add-right-neighbor, that takes as parameters both a drawing and a scale factor. You can assume that the scale factor is positive.

Hint: You will probably want to write a right-neighbor procedure that also takes as parameters both a drawing and a scale factor.

Once again, you should make sure to test a variety of cases, including scale factors both smaller and larger than 1.

(c) Document and implement a procedure, add-left-neighbor that behaves much like add-right-neighbor except that, he neighbor is on the left, rather than the right.

(d) Document and implement a procedure, add-bottom-neighbor that behaves much like add-right-neighbor, except that (i) the neighbor is below the original drawing, rather than to the side, and (ii) the center of the neighbor is directly below the center of the original drawing.

Problem 3: Drawing a Checkerboard, Concisely

Using the drawings-as-values model, write a program to create a drawing of a checkerboard called checkerboard. Your drawing, when rendered, should give an 8x8 grid of squares in alternating black and red colors; each square on the checkerboard should have a side length of 32. Your code should take advantage of the many similar elements in this figure.

We should be able to render your checkerboard drawing with the following command.

> (image-show (drawing->image checkerboard 256 256))

Recall that the drawings-as-values model includes procedures such as scale-drawing, hshift-drawing, recolor-drawing, and drawing-group.

Problem 4: Combining Compound Transformations

You have now developed three procedures that take one drawing as input and create something a bit more interesting by combining that input drawing with a variant of that drawing (a gray shadow, a scaled version).

When you combine these procedures, you should be able to achieve some fairly complex results. For example, you may note that add-right-neighbor creates two copies of a drawing. If we then call add-bottom-neighbor on that new drawing, we end up with four copies of the original drawing. Such a nested expression might look like the following.

  (add-bottom-neighbor 
   0.50
   (add-right-neighbor 
    0.65 
    red-eye))

(a) Design your own simple drawing, preferably not more complex than our red eye or blue i.

(b) Create a nested expression that takes your simple drawing and makes a more interesting and complex drawing by applying the various shadow and neighbor procedures in some combination. Your expression should nest at least two levels deep (as in the example above) and feature at least a total of four shadow or neighbor procedure calls.

(c) Write a command to render your drawing on an appropriately-sized canvas.

Important Evaluation Criteria

The first criterion we will use in evaluating your work is correctness. In particular, we will check to ensure that each program or procedure generates an appropriate image.

The second criterion we will use in evaluating your work is conciseness. That is, we will look to see whether your code is short or long for the problem. We will not differentiate short and long variable names, so please use comprehensible names.

The final criterion we will use in evaluating your work is generality. That is, for questions where you are asked to write a procedure, we will look at whether your procedure behaves correctly in a variety of situations.

Extra Credit Opportunities

We may award extra credit for the most concise code to construct a checkerboard.

We may award extra credit for particularly interesting solutions to the final problem.


Jerod Weinman

Copyright © 2007-2014 Janet Davis, Matthew Kluber, Samuel A. Rebelsky, and Jerod Weinman. (Selected materials copyright by John David Stone and Henry Walker and used by permission.)

This material is based upon work partially supported by the National Science Foundation under Grant No. CCLI-0633090. Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.

Creative Commons License This work is licensed under a Creative Commons Attribution-NonCommercial 3.0 Unported License .