Summary:
In this laboratory, you will explore two of Scheme's primary
conditional control operations, if and
cond.
If statements typically have the form
(iftestconsequentalternative)
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(test0exp0) (test1exp1) ... (testnexpn) (elsealternative))
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.
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)))
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,
(, that returns true if
rgb-brighter? color1
color2)color1 is brighter than color2
and false otherwise.
c. Using rgb-brightness or
rgb-brighter? as a helper, write a procedure,
(, that returns the brighter of
rgb-brighter color1
color2)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.
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 , that returns
color)
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.
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, ( that returns whichever
of spot-closest-e
spot spot1
spot2)spot1 and spot2
is closer to spot according to the Euclidian
distance.
b. Write a procedure, ( that returns whichever
of spot-closest-t
spot spot1
spot2)spot1 and spot2
is closer to spot according to the taxicab
distance.
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.)
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.
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.
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 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).)