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

Laboratory: Conditionals

This lab is also available in PDF.

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

Contents:

Reference:

If statements typically have the form

(if test consequent alternative)

Scheme first evaluates the test. If the value of the test is false (#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. Load a moderate-sized image (no more than about 250x250) and name the loaded image picture.

b. Create a 200x200 image and name it canvas.

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

d. Zoom in on grid.

e. 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.

(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))

Exercises

Exercise 1: Comparing Brightness, Revisited

In many recent exercises, 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:
;;;   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 brightness
  (lambda (color)
    (round (* 100 (/ (+ (* .30 (rgb.red color)) 
                        (* .59 (rgb.green color)) 
                        (* .11 (rgb.blue color)))
                      255)))))

a. Using brightness, write a procedure, (brighter color1 color2), that returns the brighter of color1 and color2.

b. What do you expect the following to accomplish?

(define fave (rgb.new 192 0 192))
(image.show (image.map (lambda (color) (brighter fave color)) grid))

c. Check your result experimentally.

d. What do you expect the following to accomplish?

(define fave (rgb.new 192 192 0))
(image.show (image.map (lambda (color) (brighter fave color)) picture))

e. Check your result experimentally.

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

b. Check the effects of mapping your rgb.4grey onto your picture.

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

Exercise 3: Drawing Stripes

At times, it may be useful to create images with stripes, vertical or horizontal. How do we do so? Consider the problem of drawing vertical stripes of width 10 in alternating colors. For columns 0 through 9, we draw in the primary color. For columns 10 through 19, we draw in the alternate color. For columns 20 through 29, we draw in the primary color. For columns 30 through 39, we draw in the alternate color. We continue alternating until we reach the end of the image.

How do we express this computationally? Well, you'll note that we have a repeating pattern every twenty columns. Whenever we have repeating patterns, we think of modulo. So, we can start by computing the modulo of the column and 20. Now, we are left with numbers in the range 0 to 19. The values 0 to 9, inclusive, are in the primary color. The values 10 to 19, inclusive, are in the alternate color.

a. Write a predicate, (primary-column? column), that returns #t if the given column should be colored in the primary color and #f if the given column should be colored in the alternate color. For example,

> (primary-column? 5)
#t
> (primary-column? 87)
#t
> (primary-column? 93)
#f
> (primary-column? 32)
#f

b. Write a procedure, (column-color column) that returns the color black for primary columns and the color red for alternate columns.

c. Create a series of stripes with

(region.compute-pixels! canvas 0 0 199 199 
  (lambda (pos) (column-color (position.col pos))))

d. Rewrite column-color to choose between three colors (say, red, black, and white) and then create a new series of stripes. In this rewrite, you will no longer be able to use primary-column?. (You may want to think about why you can't use it.)

e. Change the call to region.compute-pixels! so that it creates striped rows, rather than striped columns.

Exercise 4: Some Shapes

As you may recall from the reading, there are a variety of ways we can use conditionals to draw pictures, most typically by drawing in one color if some condition holds and another color if a condition does not hold. Suppose we've prepared to draw by defining a few colors (and the square procedure) as follows.

(define c0 (rgb.new 255 255 255))
(define c1 (rgb.new 255 0 255))
(define c2 (rgb.new 255 0 0))
(define c3 (rgb.new 0 255 255))
(define square (lambda (x) (* x x)))

a. What image do you expect the following code to create?

(region.compute-pixels! canvas 0 0 199 199
  (lambda (pos)
    (if (< (position.row pos) (- 100 (position.col pos))) c1 c0)))

b. Check your answer experimentally.

c. What image do you expect the following code to create?

(region.compute-pixels! canvas 0 0 199 199
  (lambda (pos)
    (if (< (position.row pos) (+ -75 (position.col pos))) c2 c0)))

d. Check your answer experimentally.

e. What image do you expect the following code to create?

(region.compute-pixels! canvas 0 0 199 199
  (lambda (pos)
    (if (>= (square 50)
            (+ (square (- (position.col pos) 20)) 
               (square (- (position.row pos) 60))))
        c3 c0)))

f. Check your answer experimentally.

g. In the reading, we noted that each of the procedures given in the exercise above eliminates any traces of the previous drawing. Try redefining c0 as transparent and then running each of the instructions in sequence. What happens?

Exercise 5: Drawing Lines and Regions

How did we come up with the formulae for the images in the previous problem? For the triangles, it was mostly a matter of working out lines, since we can use lines to border triangular region. So, let's explore that issue.

You should remember that the general formula of a non-vertical line is y = mx+b. We can therefore draw something that's a bit like a line with region.compute-pixels! and a translation of that formula into Scheme. For example, to draw a diagonal line with a slope of 1 in the upper-left-hand corner, we might write

(region.compute-pixels! 
 canvas 0 0 199 199
 (lambda (pos)
  (if (= (position.row pos) (- 100 (position.col pos))) c1 c0)))

Why is the slope 1, rather than -1? Because the image coordinate system is upside-down.

If we replace the = with < or >=, we can color large regions of the image.

a. Using this technique, draw a black line from the upper-left corner of canvas to the lower-right corner of canvas.

(region.compute-pixels!
 canvas 0 0 199 199
 (lambda (pos)
   (if (= (position.row pos) (+ ___ (* ___ (position.col pos))))
       color.black transparent)))

b. Using this technique, draw a green line from the upper-right corner of canvas to the lower-left corner of canvas.

(region.compute-pixels!
 canvas 0 0 199 199
 (lambda (pos)
   (if (= (position.row pos) (+ ___ (* ___ (position.col pos))))
       color.green transparent)))

c. Using this technique, draw a red line from midway down the left side of canvas to the upper-right corner of canvas. (You may have to try a few different formulae to get it right.)

(region.compute-pixels!
 canvas 0 0 199 199
 (lambda (pos)
   (if (= (position.row pos) (+ ___ (* ___ (position.col pos))))
       color.red transparent)))

d. Using this technique, draw a blue line from midway across the bottom side of canvas to the upper-right corner of canvas. (You may have to try a few different formulae to get it right).

(region.compute-pixels!
 canvas 0 0 199 199
 (lambda (pos)
   (if (= (position.row pos) (+ ___ (* ___ (position.col pos))))
       color.blue transparent)))

e. You may note that some of the lines you've draw look incomplete. That is, not all the pixels in the line are connected. The problem, of course, stems from our attempt to represent a continuous line using a discrete set of pixels. We will look at some solutions to this problem later in the semester. For now, you may find that it's easier to explore some slopes by filling in regions, and not just lines. Pick one of your formulae from c or d, and try the following.

(region.compute-pixels!
 canvas 0 0 199 199
 (lambda (pos)
   (if (<= (position.row pos) (+ ___ (* ___ (position.col pos))))
       color.brown transparent)))

(region.compute-pixels!
 canvas 0 0 199 199
 (lambda (pos)
   (if (>= (position.row pos) (+ ___ (* ___ (position.col pos))))
       color.tan transparent)))

For Those With Extra Time

Extra 1: 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.

(define square (lambda (x) (* x x)))
(define euclidean-distance
  (lambda (pos1 pos2)
    (sqrt (+ (square (- (position.col pos1) (position.col pos2)))
             (square (- (position.row pos1) (position.row pos2)))))))

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.

(define taxicab-distance
  (lambda (pos1 pos2)
    (+ (abs (- (position.col pos1) (position.col pos2)))
       (abs (- (position.row pos1) (position.row pos2))))))

Now, let's use those distance procedures to create some interesting drawings.

a. Write a procedure, (pattern-e pos) that returns

b. Create a new image by applying this procedure at every position in canvas.

c. Write a procedure, (pattern-t pos), similar to pattern-e, except that it uses taxicab distance rather than Euclidean distance.

d. Create a new image by applying this procedure at every position in canvas.

Extra 2: Checkerboards

Note that making a checkerboard-like image is much like making a set of stripes. However, instead of doing a whole column at a time, one must look at the columns and the rows.

a. Write a procedure, (checkerboard-color pos) that, given a position, returns either black (the primary) or red (the secondary) according to the rules above. In your answer, strive to be as concise as possible.

b. Use your procedure, along with region.compute-pixels!, to draw a simple checkerboard.

Extra 3: 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 4: Drawing Triangles

In the earlier exploration of drawing shapes, we found that it was relatively easy to draw a triangle in the corner of an image by figuring out the slope of a line and restricting the pixels to be above the line.

To draw a more complex triangle, it may be necessary to figure out the formulae of three lines, one for each side of the triangle. We then build the triangle by drawing the desired color only for the pixels that are on the appropriate side of each side.

a. Write instructions to draw an isosceles triangle with a horizontal base of length 20 and a height 20 in the color green. Put the base of the triangle at row 120 and the point of the triangle in column 70.

b. Write instructions to draw a right triangle whose base is 30, and whose height is 60 in a color of your choice. Put the base of the triangle along row 130 and the left edge

Explorations

Exploration 1: Two-Color Computed Images

Using the technique of when this expression holds, draw in one color, otherwise draw in another color, see what interesting two-color images you can create.

Exploration 2: Multi-color Checkerboards

If you completed extra problem 2, you've learned a technique for drawing two-color checkerboards. Extend that technique to draw checkerboards in more colors.

Notes

 

History

 

Disclaimer: I usually create these pages on the fly, which means that I rarely proofread them and they may contain bad grammar and incorrect details. It also means that I tend to update them regularly (see the history for more details). Feel free to contact me with any suggestions for changes.

This document was generated by Siteweaver on Mon Dec 3 09:54:46 2007.
The source to the document was last modified on Wed Sep 19 16:24:07 2007.
This document may be found at http://www.cs.grinnell.edu/~rebelsky/Courses/CS151/2007F/Labs/conditionals-lab.html.

You may wish to validate this document's HTML ; Valid CSS! ; 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.