Fundamentals of Computer Science I: Media Computing (CS151.01 2008S)

Laboratory: Conditionals


Summary: In this laboratory, you will explore two of Scheme's primary conditional control operations, if and cond.

Reference

If statements typically have the form

(if test consequent alternative)

Scheme first evaluates the test. If the value of the test is false (that is, #f), it evaluates the alternative and returns the value. If the value of the test is truish (that is, anything that is not false), it evaluates the consequent and returns its value.

Cond statements typically have the form

(cond
  (test0 exp0)
  (test1 exp1)
  ...
  (testn expn)
  (else alternative))

Scheme evaluates each test in turn until one is truish. It then evaluates the corresponding expression and returns its value. If none of the tests is truish, then it evaluates the alternative and returns its value.

Preparation

a. Create and show a new 200x200 image and name it canvas.

b. Create and show a new 3x3 image and name it grid.

c. Zoom in on grid. (I prefer a scale factor of 1600%; you might prefer 800%.)

d. Fill in the pixels of grid as follows. Note that you may want to open this lab in a Web browser and cut and paste these lines.

(image-set-pixel! grid 0 0 (rgb-new 192 0 0))
(image-set-pixel! grid 1 0 (rgb-new 192 0 192))
(image-set-pixel! grid 2 0 (rgb-new 0 0 192))
(image-set-pixel! grid 0 1 (rgb-new 192 192 0))
(image-set-pixel! grid 1 1 (rgb-new 0 192 192))
(image-set-pixel! grid 2 1 (rgb-new 255 255 255))
(image-set-pixel! grid 0 2 (rgb-new 0 192 0))
(image-set-pixel! grid 1 2 (rgb-new 0 0 0))
(image-set-pixel! grid 2 2 (rgb-new 192 192 192))

e. Open the reference page for drawings in a separate window or pane.

f. If you have not already done so, add the basic spot procedures to your library. These procedures are:

;;; Procedure:
;;;   spot-new
;;; Parameters:
;;;   col, an integer
;;;   row, an integer
;;;   color, a color (name, RGB, etc.)
;;; Purpose:
;;;   Create a new spot.
;;; Produces:
;;;   spot, a spot
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   (spot-col spot) = col
;;;   (spot-row spot) = row
;;;   (spot-color spot = color
(define spot-new
  (lambda (col row color)
    (list col row color)))

;;; Procedure:
;;;   spot-col
;;; Parameters:
;;;   spot, a spot
;;; Purpose:
;;;   Extract the col from a spot.
;;; Produces:
;;;   col, an integer
(define spot-col
  (lambda (spot)
    (car spot)))

;;; Procedure:
;;;   spot-row
;;; Parameters:
;;;   spot, a spot
;;; Purpose:
;;;   Extract the row from a spot.
;;; Produces:
;;;   row, an integer
(define spot-row
  (lambda (spot)
    (cadr spot)))

;;; Procedure:
;;;   spot-color
;;; Parameters:
;;;   spot, a spot
;;; Purpose:
;;;   Extract the color from a spot.
;;; Produces:
;;;   color, an integer
(define spot-color
  (lambda (spot)
    (caddr spot)))

Exercises

Exercise 1: Comparing Brightness, Revisited

Recently, we've considered a number of activities that required us to work with the brightness of colors. We typically compute brightness using an expression which can be encapsulated in a procedure as follows:

;;; Procedure:
;;;   rgb-brightness
;;; Parameters:
;;;   color, an RGB color
;;; Purpose:
;;;   Computes the brightness of color on a 0 (dark) to 100 (light) scale.
;;; Produces:
;;;   b, an integer
;;; Preconditions:
;;;   color is a valid RGB color.  That is, each component is between
;;;     0 and 255, inclusive.
;;; Postconditions:
;;;   If color1 is likely to be perceived as lighter than color2,
;;;     then (brightness color1) > (brightness color2).
(define rgb-brightness
  (lambda (color)
    (round (* 100 (/ (+ (* 0.30 (rgb-red color))
                        (* 0.59 (rgb-green color))
                        (* 0.11 (rgb-blue color)))
                      255)))))

a. Add rgb-brightness to your library and then load your library.

b. Using rgb-brightness as a helper, write a procedure, (rgb-brighter? color1 color2), that returns true if color1 is brighter than color2 and false otherwise.

c. Using rgb-brightness or rgb-brighter? as a helper, write a procedure, (rgb-brighter color1 color2), that returns the brighter of color1 and color2.

d. What do you expect the following to accomplish?

(define change-pixel!
  (lambda (image col row)
    (image-set-pixel! image col row
                      (rgb-brighter (image-get-pixel image col row)
                                    (image-get-pixel image 0 0)))))
(change-pixel! grid 0 0)
(change-pixel! grid 1 0)
(change-pixel! grid 2 0)
(change-pixel! grid 0 1)
(change-pixel! grid 1 1)
(change-pixel! grid 2 1)
(change-pixel! grid 0 2)
(change-pixel! grid 1 2)
(change-pixel! grid 2 2)

e. Check your answer experimentally.

f. Add rgb-brighter? and rgb-brighter to your library.

Exercise 2: Shades of Grey

Consider the following four colors:

(define grey0 (rgb-new 0 0 0))
(define grey1 (rgb-new 96 96 96))
(define grey2 (rgb-new 192 192 192))
(define grey3 (rgb-new 255 255 255))

a. Using if statements (and not cond statements), write a procedure, (rgb-4grey color), that returns

  • grey0, if the brightness of color is less than 25;
  • grey1, if the brightness of color is at least 25 but less than 50;
  • grey2, if the brightness of color is at least 50 but less than 75; or
  • grey3, if the brightness of color is at least 75.

b. Rewrite rgb-4grey so that it uses cond rather than if.

Exercise 3: Distance-Based Computations

When working with positions, we often concern ourselves with the distance between pairs of positions. There are two common ways to define this distance. We can use a standard Euclidean distance, the length of the shortest line between the two points.

;;; Procedure:
;;;   euclidean-distance
;;; Parameters:
;;;   col1, a real number
;;;   row1, a real number
;;;   col2, a real number
;;;   row2, a real number
;;; Purpose:
;;;   Computes the euclidean distance between (col1,row1) and
;;;   (col2,row).
;;; Produces:
;;;   distance, a real number
(define euclidean-distance
  (lambda (col1 row1 col2 row2)
    (sqrt (+ (square (- col2 col1)) (square (- row2 row1))))))

We can also define distance in terms of the sum of the horizontal and vertical distances of the two positions. Because this distance mimics the way a taxi might drive in a city laid out on a grid (well, as long as you're not playing Crazy Taxi), we call this distance the “taxicab distance”.

;;; Procedure:
;;;   taxicab-distance
;;; Parameters:
;;;   col1, a real number
;;;   row1, a real number
;;;   col2, a real number
;;;   row2, a real number
;;; Purpose:
;;;   Computes the taxicab distance between (col1,row1) and
;;;   (col2,row).
;;; Produces:
;;;   distance, a real number
(define taxicab-distance
  (lambda (col1 row1 col2 row2)
    (+ (abs (- col2 col1)) (abs (- row2 row1)))))

a. Write a procedure, (spot-closest-e spot spot1 spot2) that returns whichever of spot1 and spot2 is closer to spot.

b. Write a procedure, (spot-closest-t spot spot1 spot2) that returns whichever of spot1 and spot2 is closer to spot.

Exercise 4: Rendering Drawings

Here is the drawing-render! procedure from the reading.

;;; Procedure:
;;;   drawing-render!
;;; Parameters:
;;;   drawing, a drawing value
;;;   image, an image id
;;; Purpose:
;;;   Render drawing on the image.
;;; Produces:
;;;   [Nothing; called for side effect]
;;; Preconditions:
;;;   drawing can be rendered on image.  
;;; Postconditions:
;;;   drawing has been rendered on image.
(define drawing-render!
  (lambda (drawing image)
    (cond
      ((equal? (drawing-type drawing) 'rectangle)
       (context-set-fgcolor! (drawing-color drawing))
       (image-select-rectangle! image selection-replace
                                (drawing-left drawing)
                                (drawing-top drawing)
                                (drawing-width drawing)
                                (drawing-height drawing))
       (image-fill! image))
      ((equal? (drawing-type drawing) 'ellipse)
       (context-set-fgcolor! (drawing-color drawing))
       (image-select-ellipse! image selection-replace
                              (drawing-left drawing)
                              (drawing-top drawing)
                              (drawing-width drawing)
                              (drawing-height drawing))
       (image-fill! image))
      (else
       (image-draw-line! image 0 0 (image-width image) (image-height image))))))

And here are two sample drawings.

(define d1
  (drawing-hshift
   (drawing-vshift
    (drawing-scale
     (drawing-recolor
      drawing-unit-square
      "red")
     50)
    40)
   30)
  )

(define d2
  (drawing-vscale
   (drawing-hscale
     (drawing-recolor
      drawing-unit-circle
      "green")
    60)
   120)
  )

a. Test drawing-render! to verify that it does, in fact, render both drawings.

b. As you may have observed, we don't immediately see the rendered drawing. Update drawing-render! so that, after rendering, it updates the display and selects nothing.

c. Extend drawing-render! so that it outlines the drawing in black.

d. Extend drawing-render! so that it outlines the drawing in black if the drawing is a light color and outlines the drawing in grey if the drawing is a dark color. (You can choose your own metric for dark and light.)

For Those With Extra Time

Those with extra time may choose to do the extra problems, which focus on programming tasks, or the explorations, which focus on the application of procedures to image creation.

Extra 1: Shades of Grey, Revisited

As you may recall from the reading on Boolean values and predicate procedures, it is possible to use and and or to achieve conditional behavior. The subsequent reading on conditionals provides some additional information on the use of and and or.

Using those techniques, rewrite rgb-4grey so that it uses neither if nor cond. That is, you'll need to find ways to express the same outcome using and and or.

Extra 2: Safely Rendering Drawings

One problem with drawing-render! is that it causes an error if we try to render a drawing outside of the image. For example,

> (define canvas (image-new 10 10))
canvas
> (drawing-render! (drawing-hshift drawing-unit-circle -2) canvas)
Error: image-select-ellipse!: selection is outside of the bounds of the image 
> (drawing-render! (drawing-hshift drawing-unit-circle 11) canvas)
Error: image-select-ellipse!: selection is outside of the bounds of the image 

Rewrite drawing-render! to that it only renders drawings that fall within the bounds of the canvas.

Explorations

Explorations are intended for students interested in further exploring the design aspects of these techniques. They also provide students who finish early with extra activities that may challenge them in different ways.

You've already noted that drawing-render! can do more than simply select an area and then fill it. For example, we can stroke the drawing after filling it. (We could even stroke it multiple times.)

Write a procedure, drawing-render-excessively!, that renders a drawing in a more interesting manner. You might, for example, stroke the drawing with a large brush, a medium brush, and a small brush, each of different colors. You might draw a contrasting smaller shape within the object. (E.g., within a square of edge length d, you can draw a circle of diameter d; within a circle of diameter d, you can draw a square of edge length (/ d (sqrt 2).)

Notes

Creative Commons License

Samuel A. Rebelsky, rebelsky@grinnell.edu

Copyright (c) 2007-8 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.