Fundamentals of Computer Science I: Media Computing (CS151.02 2007F)

Assignment 13: Saving Shape-Based Drawings in Files


Due: 4 p.m., Tuesday, 13 November 2007

Summary: In this assignment, you will develop procedures that allow us to use files to store information about simple shapes and to reproduce those shapes from the stored information.

Purposes: To give you more experience with files. To help you think about yet more representations of images.

Expected Time: Two to three hours.

Collaboration: We encourage you to work in groups of size three. You may, however, work alone or work in a group of size two or size four. You may discuss this assignment with anyone, provided you credit such discussions when you submit the assignment.

Submitting: Email your answer to . The title of your email should have the form CSC151.02 2007F Assignment 13: Shape-Based Drawing and should contain your answers to all parts of the assignment. Scheme code should be in the body of the message.

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

Preliminaries

As you've just seen in the exam, it can be useful to describe images as a sequence of simple drawing objects, such as circles and squares. There are many ways that we can represent such objects. In Scheme, one of the most natural techniques is to use a list. However, it can be difficult to make those lists persist between invocations of Scheme. We might also want to provide a slightly more human-readable approach to representing these objects.

Here's one such technique: We use a file to store the drawing objects in an image. Each line of the file represents one object. A line begins with the name of the object, such as "filled ellipse", "empty circle", "line", or "filled square". The remaining values on the line depend on the kind of object.

  • An empty ellipse, which is stroked, is followed by the color, the brush, the left edge, the top edge, the width, and the height of the ellipse.
  • A filled ellipse, which is filled, is followed by the color, the left edge, the top edge, the width, and the height of the ellipse.
  • An empty circle, which is stroked, is followed by the color, the brush, the left edge, the top edge, and the diameter of the circle.
  • A filled circle, which is filled, is followed by the color, the left edge, the right edge, and the diameter of the circle.
  • An empty rectangle, which is stroked, is followed by the color, the brush, the left edge, the top edge, the width, and the height of the rectangle.
  • A filled rectangle, which is filled, is followed by the color, the left edge, the top edge, the width, and the height of the rectangle.
  • An empty square, which is stroked, is followed by the color, the brush, the left edge, the top edge, and the side length of the square.
  • A filled square, which is filled, is followed by the color, the left edge, the top edge, and the side length of the box.
  • A line is followed by the color, the brush, the starting column, the starting row, the ending column, and the ending row of the line.

For example, here's a simple drawing:

"empty ellipse" "red" "Circle (03)"  10 10 80 40
"filled square" "rich blue" 30 30 80
"line" "green" "Circle Fuzzy (11)" 0 90 100 20

The first goal is to read these values from a file and draw them. Here's a start.

(define image.read-object!
  (lambda (image port)
    (let* ((type (read port))
           (stroked? (list.contains? 
                       (list "empty ellipse" "empty circle" 
                             "empty rectangle" "empty square" 
                             "line")
                       type))
           (color (read port))
           (brush (if stroked? (read port) (envt.get-brush))))
      (cond
        ((list.contains? (list "empty ellipse" "filled ellipse") type)
         (let* ((left (read port))
                (top (read port))
                (width (read port))
                (height (read port)))
           (image.select-ellipse! image 
                                  selection.replace
                                  left top width height)
           (if stroked?
               (image.stroke! image)
               (image.fill! image))
           (image.select-nothing! image)))
        ((list.contains? (list "empty square" "filled square") type)
         (let* ((left (read port))
                (top (read port))
                (side (read port)))
           (image.select-rectangle! image 
                                     selection.replace
                                     left top side side)
           (if stroked?
               (image.stroke! image)
               (image.fill! image))
           (image.select-nothing! image)))
        ((equal? type "line")
         (let* ((start-col (read port))
                (start-row (read port))
                (end-col (read port))
                (end-row (read port)))
           (image.draw-line! image start-col start-row end-col end-row)))))))

You can find the helper procedures at the end of this assignment.

Assignment

a. Right now, image.read-object! reads the color to use and the brush to use but then ignores both. Update the procedure so that it sets the brush and color before drawing.

b. Right now, image.read-object! only does about half of the desired shapes. Extend it to do the other shapes.

c. Write a procedure, (image.load-objects! image filename), that reads and renders every object in the named file.

d. Write procedures that let us write each kind of object from Scheme. For example,

(define empty-rectangle.write
  (lambda (port color brush left top width height)
    (write "empty rectangle" port)
    (display " " port)    
    (write (if (rgb? color) (rgb->cname color) color) port)
    (display " " port)
    (write brush port)
    (display " " port)
    (write left port)
    (display " " port)
    (write top port)
    (display " " port)
    (write width port)
    (display " " port)
    (write height port)
    (newline port)))

Important Evaluation Criteria

We will evaluate your assignment primarily on its correctness: Does it correctly read and write these kinds of values. However, since this program can be written with very repetitious code, we will also look to ways to avoid repetition. (You'll note that the sample code avoids some repetition by doing only one selection for related object. However, it also repeats some code. Can you do better?)

Utility Procedures

You are likely to find the following procedures useful.

(define list.contains?
  (lambda (lst val)
    (and (not (null? lst))
         (or (equal? (car lst) val)
             (list.contains? (cdr lst) val)))))

Creative Commons License

Samuel A. Rebelsky, rebelsky@grinnell.edu

Copyright 2007 Janet Davis, Matthew Kluber, and Samuel A. Rebelsky. (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.

This work is licensed under a Creative Commons Attribution-NonCommercial 2.5 License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/2.5/ or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA.