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:
- No line may exceed 80 columns in length. How? Go to Racket Preferences > Editing > General Editing, and enable the “maximum character width guard.”
- The program must be properly indented, including the comments. How? Select some code and press Tab to automatically reindent just that code, or press Cmd/Ctl + I to reindent the whole file.
- Function and constant names must be descriptive, and argument names often should be.
- The program must follow the same naming conventions
as the textbook.
What?
For example, function names are
lower-kebab-case
, datatype names areUpper-kebab-case
orUpperCamelCase
, and constant names areSCREAMING-KEBAB-CASE
. In all style butUpperCamelCase
, words are separated by hyphens (-
).
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:
- The shape can be changed to square (by pressing the s key), triangle (t), or back to circle (c).
- The color can be changed to red (r), green (g), blue (b), black (k), and white (w).
- The size can be increased (+) and decreased (-) by one pixel at a time. What’s the size though? For a circle the size is the diameter, not the radius; for a square or triangle it’s the side length.
Additionally, a “cursor” matching the current brush is displayed and follows the mouse position. Changing the brush changes this cursor.
Steps
-
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.
-
Write, in
#;
-style comments, templates for processing theShape
,Brush
, andPaintWorld
data definitions. -
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 ofBrush
, itsShape
, andPosn
requires a different template for structural decomposition, you should be defining 3 to 5 functions right here if we countrender-brush-at
itself, its direct helpers, and helpers-of-helpers. . -
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 thePaintWorld
and usesrender-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.
-
Design a function
handle-mouse-event : PaintWorld Number Number MouseEvent -> PaintWorld
to react to mouse events. It has two tasks to accomplish:- It must remember the most recent mouse position by updating
the
mouse
field of thePaintWorld
. - When the mouse clicks (detected via the mouse event
"button-up"
) or drags ("drag"
), it must update thePaintWorld
’simage
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 tobig-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. - It must remember the most recent mouse position by updating
the
-
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:Key Resulting Change s shape to square c shape to circle t shape to triangle r color to red g color to green b color to blue k color to black w color 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 theKeyEvent
, with a clause for each relevant key and anelse
clause to ignore other keystrokes. In order to cleanly update the variousBrush
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 theBrush
, passing theKeyEvent
to a helper that updates eachBrush
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
-
Have
-
Design a function
handle-key-event : PaintWorld KeyEvent -> PaintWorld
that usesupdate-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 tobig-bang
to complete your program:(big-bang WORLD0 [to-draw render-paint-world] [on-mouse handle-mouse-event] [on-key handle-key-event])
-
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:
- design (how carefully your programming process has followed the Design Recipe),
- correctness (how closely your code’s behavior matches its specification, which is this web page),
- test coverage (whether every expression in every
function is
checked by some automated test, as with
check-expect
), and - 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.