EECS 111: Homework 2

Due: Tuesday, April 23 at 11:59 PM

Please note: This assignment is to be completed with your new assigned partner

Purpose

The goals of this assignment are for you to gain practice using structures and to start writing larger programs.

Design & Code Style

Design and code style are both essential to effective programming, and thus each will be graded. For design, we expect you to follow the six-step Design Recipe. For style, it means that:

Overview

In this assignment you will write a painting program. However, you are not yet responsible for the overall design. We will guide you step by step.

The painting program initially displays an empty 400x400 window; clicking and dragging the mouse paints in the window. Initially each click adds a solid black circle whose diameter is 10, but the paint brush can be adjusted in three ways:

Additionally, a “cursor” matching the current brush is displayed and follows the mouse position. Changing the brush changes this cursor.

Steps

  1. Download the starter code. It provides you with data definitions for paintings, shapes, brushes, and your world state, as well as several constants. Read the data definitions carefully, because it’s essential that you fully understand them and how they will represent the state of the paint program.

  2. Write, in #;-style comments, templates for processing the Shape, Brush, and PaintWorld data definitions.

  3. Design a function render-brush-at : Brush Posn Painting -> Painting that adds an image of the given brush at the given position in the given painting. Be sure to test your function thoroughly. Hint: The design strategy here should be function composition. Keep it short, and remember that each function should serve one discrete, easy-to-describe purpose. In particular, since each of Brush, its Shape, and Posn requires a different template for structural decomposition, you should be defining 3 to 5 functions right here if we count render-brush-at itself, its direct helpers, and helpers-of-helpers. .

  4. Design a function render-paint-world : PaintWorld -> Image that completes rendering the world by placing the brush in the painting image at the last seen mouse position. (That is, it structurally decomposes the PaintWorld and uses render-brush-at.)

    Now that you have a rendering function you can call big-bang:

    (big-bang WORLD0
      [to-draw render-paint-world])

    Of course, at this point your program won’t do anything but render the initial cursor at (0, 0). However, the path we suggest for writing the program will build up functionality incrementally, which means you can continue to try it out along the way.

  5. Design a function handle-mouse-event : PaintWorld Number Number MouseEvent -> PaintWorld to react to mouse events. It has two tasks to accomplish:

    1. It must remember the most recent mouse position by updating the mouse field of the PaintWorld.
    2. When the mouse clicks (detected via the mouse event "button-up") or drags ("drag"), it must update the PaintWorld’s image field by adding the image of the current brush at the current mouse position.

    We suggest implementing either one or the other piece of functionality first, then interactively testing it by adding an [on-mouse handle-mouse-event] clause to big-bang. Once you’ve added both pieces of functionality, you should be able to paint using 10-pixel black circles, and a 10-pixel black circle should follow the mouse pointer acting as a cursor.

  6. Design a function update-brush-by-key : Brush KeyEvent -> Brush that updates the brush based on the given key event. That is, the following keys should result in the following brush changes:

    KeyResulting Change
    sshape to square
    cshape to circle
    tshape to triangle
    rcolor to red
    gcolor to green
    bcolor to blue
    kcolor to black
    wcolor to white
    +size increase by 1
    -size decrease by 1

    There are two ways we suggest decomposing this, either of which you may choose:

    • Have update-brush-by-key perform structural decomposition on the KeyEvent, with a clause for each relevant key and an else clause to ignore other keystrokes. In order to cleanly update the various Brush fields, define three helpers:
      update-brush-shape : Brush Shape -> Brush
      update-brush-color : Brush Color -> Brush
      update-brush-size  : Brush Number -> Brush
    • Have update-brush-by-key perform structural decomposition on the Brush, passing the KeyEvent to a helper that updates each Brush field before reconstructing the brush. The helpers are:
      update-shape-by-key : Shape KeyEvent -> Shape
      update-color-by-key : Color KeyEvent -> Color
      update-size-by-key  : Number KeyEvent -> Number
  7. Design a function handle-key-event : PaintWorld KeyEvent -> PaintWorld that uses update-brush-by-key to update the brush based on each given key event. Then add the clause [on-key handle-key-event] to your call to big-bang to complete your program:

    (big-bang WORLD0
      [to-draw   render-paint-world]
      [on-mouse  handle-mouse-event]
      [on-key    handle-key-event])
  8. Be sure you have followed the Design Recipe on all functions. This includes explicit identification of the design strategy and complete test coverage.

Evaluation

Your code will be evaluated for:

  1. design (how carefully your programming process has followed the Design Recipe),
  2. correctness (how closely your code’s behavior matches its specification, which is this web page),
  3. test coverage (whether every expression in every function is checked by some automated test, as with check-expect), and
  4. style (how readable it is, as measured by the rules listed above).

Turn In

Please write all your code in the provided starter file (paint.rkt) and submit it through Canvas.