Functional Problem Solving (CSC 151 2015F) : Assignments

Assignment 5: Drawing with Conditionals and Anonymous Procedures


Due: 10:30 p.m., 6 October 2015

Summary: In this assignment, you will use anonymous procedures and conditionals to generate interesting drawings with repeating patterns.

Purposes: To further practice building drawings programmatically, this time using conditionals and anonymous procedures to produce drawings with interesting patterns.

Collaboration: You must work with your assigned partners on this assignment. You must collaborate on every problem - do not break up the work so that one person works on problem 1, another on problem 2, and another on problem 3. (The “don't break up the work” policy applies to every assignment. This note is just a reminder.) You may discuss this assignment with anyone, provided you credit such discussions when you submit the assignment.

Submitting: Send your answer to . The title of your email should have the form CSC 151.01 Assignment 5: Conditionals and should contain your answers to all parts of the assignment. I prefer that you put your answers in the body of the message, rather than as an attachment.

So that this assignment is a learning experience for everyone, we may spend class time publicly critiquing your work.

Assignment

Problem 1: Drawing Stripes

Write and document a procedure (make-stripes num color1 color2) that draws num horizontal stripes alternating between colors color and color2. This procedure should produce a drawing composed of one pixel wide, one pixel tall unit squares with the upper left corner at 0,0 and the lower right corner at 1,num.

Scaling the result of your make-stripes procedure allows you to produce stripes of any height and width. One possible use of this procedure is to draw the Catalan flag with the following code:

(image-show 
 (drawing->image 
  (hscale-drawing
   100 
   (vscale-drawing
    8 
    (make-stripes 9 "yellow" "red"))) 100 72))

Problem 2: Drawing Graphs

Write and document a procedure (make-plot fun) that produces a drawing of a function fun over a range of x values from 0 to 200, and y values from -50 to 50. Your make-plot procedure should plot at least 20 different points for the function between x values of 0 and 200. For each point, compute a y value with (fun x-value) and place a small circle in the image. A y value of -50 should be at the bottom of your drawing, and a y value of 50 should be at the top. Color each point blue if the y value is positive and red if the y value is negative.

Here are a few example plots that may help you test your make-plot procedure:

(image-show (drawing->image (make-plot (lambda (x) (- (/ x 2) 50))) 200 100))

(image-show (drawing->image (make-plot (lambda (x) (* 50 (cos (/ x (* 4 pi)))))) 200 100))

(image-show (drawing->image (make-plot (lambda (x) (* 50 (cos (/ x (* 4 pi)))))) 200 100))

Problem 3: Sampling Images

In your work with image-variant, you developed a variety of “image filters”, procedures that changed an image pixel-by-pixel. However, it can be equally interesting to work with larger portions of an image. You've already seen that the image-select-rectangle! and image-fill! procedures can be used to fill in a portion of the image with a single color. Let's explore ways in which we might transform an image by filling in small portions of the image with colors based on the existing colors of the image.

In order to achieve this result, you'll need to have a way to “sample” a region of the image. Two procedures will help you with sampling. (image-get-pixel image col row) will extract the color from an image at the given column and row and return it as an integer-encoded RGB color. (random n) will give you an unpredictable number between 0 and n-1, inclusive.

a. Write a procedure, (sample-and-replace-a! image left top width height), that randomly samples one pixel from the rectangular area of image described by left, top, width, and height, and replaces that area with a rectangle that is colored the same as the sampled color.

You'll find the following procedures useful to apply sample-and-replace-a! to a grid of rectangles in the image.

;;; Procedure:
;;;   sample-and-replace-column!
;;; Parameters:
;;;   image, an image
;;;   sample-and-replace-rectangle!, a procedure
;;;   left, a non-negative integer
;;;   width, a non-negative integer
;;;   rows, a non-negative integer
;;; Purpose:
;;;   Breaks the column of the given image into rows equally spaced
;;;   blocks and calls sample-and-replace-rectangle on each of them.
;;; Produces:
;;;   image, the same image, now modified
;;; Preconditions:
;;;   The height of the image must be evenly divisible by rows.
;;;     That is, if height = (/ (image-height image) rows), then
;;;     (integer? height).
;;;   sample-and-replace-rectangle! takes five parameters, representing
;;;     image, left, top, width, and height.
;;; Postconditions:
;;;   sample-and-replace-rectangle! has been called on an appropriate
;;;   series of rectangles. 
;;;     (sample-and-replace-rectangle! image left 0 width height)
;;;     (sample-and-replace-rectangle! image left height width height)
;;;     (sample-and-replace-rectangle! image left (* 2 height) width height)
;;;     (sample-and-replace-rectangle! image left (* 3 height) width height)
;;;     ...
;;;     (sample-and-replace-rectangle! image 
;;;                                    left (* (- rows 1) height)
;;;                                    width height)
(define sample-and-replace-column!
  (lambda (image sample-and-replace-rectangle! left width rows)
    (let ([height (/ (image-height image) rows)])
      (map
       (o (section sample-and-replace-rectangle! image left <> width height)
          (section * <> height))
       (iota rows)))
    (image-select-nothing! image)
    image))

;;; Procedure:
;;;   sample-and-replace-grid!
;;; Parameters:
;;;   image, an image
;;;   sample-and-replace-rectangle!, a procedure
;;;   cols, a non-negative integer
;;;   rows, a non-negative integer
;;; Purpose:
;;;   Breaks the image into a grid with the specified number
;;;   of columns and rows and calls sample-and-replace-rectangle!
;;;   on each rectangle in the grid.
;;; Produces:
;;;   image, the same image, now modified
;;; Preconditions:
;;;   The height of the image must be evenly divisible by rows.
;;;     Let height = (/ (image-height image) rows)
;;;     We must have (integer? height).
;;;   The width of the image must be evenly divisible by cols.
;;;     Let width = (/ (image-width  image) cols)
;;;     We must have (integer? width)
;;;   sample-and-replace-rectangle! takes five parameters, representing
;;;     image, left, top, width, and height.
;;; Postconditions:
;;;   sample-and-replace-rectangle! has been called on an appropriate
;;;   series of rectangles. 
;;;     (sample-and-replace-rectangle! image 0 0 width height)
;;;     (sample-and-replace-rectangle! image 0 height width height)
;;;     (sample-and-replace-rectangle! image 0 (* 2 height) width height)
;;;     ...
;;;     (sample-and-replace-rectangle! image 
;;;                                    0 (* (- rows 1) height)
;;;                                    width height)
;;;     (sample-and-replace-rectangle! image width 0 width height)
;;;     (sample-and-replace-rectangle! image width height width height)
;;;     (sample-and-replace-rectangle! image width (* 2 height) width height)
;;;     ...
;;;     (sample-and-replace-rectangle! image 
;;;                                    width (* (- rows 1) height)
;;;                                    width height)
;;;     ...
;;;     (sample-and-replace-rectangle! image 
;;;                                    (* (- cols 1) width) 
;;;                                    (* (- rows 1) height)
;;;                                    width height)
(define sample-and-replace-grid!
  (lambda (image sample-and-replace-rectangle! cols rows)
    (let ([width (/ (image-width image) cols)])
      (map
       (o (section sample-and-replace-column! 
                   image 
                   sample-and-replace-rectangle!
                   <>
                   width
                   rows)
          (section * <> width))
       (iota cols)))
    image))

For example, here are three calls to (sample-and-replace-grid! kitten sample-and-replace-a! 40 30). The third uses a variant of your sample-and-replace-a! that fills ellipses, rather then rectangles.

Here's an alternate that uses 16 columns and 60 rows.

b. Write a procedure, (sample-and-replace-b! image left top width height), that randomly samples three pixels from the given area and replaces it with a rectangle that is colored the same as the average of the three samples.

c. Write a procedure, (sample-and-replace-c! image left top width height), that randomly samples three pixels from the given area, finds the brightness of each, and replaces the area with a rectangle that is colored the same as pixel with the median brightness.

d. Write a procedure, (sample-and-replace-d! image left top width height), that randomly samples three pixels from the given area. If the three pixels are close to each other (say, no two pixels are more than 50 away from each other), recolor the rectangle with the average of the three colors. If the three pixels are not close ot each other, leave the area as is.

You will find the following procedure useful.

;;; Procedure:
;;;   irgb-distance
;;; Parameters:
;;;   color1, an integer-encoded RGB color
;;;   color2, an integer-encoded RGB color
;;; Purpose:
;;;   Compute the "distance" between color1 and color2 in 
;;;   RGB color space
;;; Produces:
;;;   distance, a real number
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   0 <= distance lt;= MAX-IRGB-DISTANCE
;;;   If color1 = color2, distance = 0
;;;   For any three colors, c1, c2, and c3, if c1 is likely
;;;     to be interpreted as closer to c2 than c3, then
;;;     (irgb-distance c1 c2) < (irgb-distance c1 c3)
(define irgb-distance
  (lambda (color1 color2)
    (let ([r1 (irgb-red color1)]
          [r2 (irgb-red color2)]
          [g1 (irgb-green color1)]
          [g2 (irgb-green color2)]
          [b1 (irgb-blue color1)]
          [b2 (irgb-blue color2)])
      (sqrt (+ (square (- r2 r1)) (square (- g2 g1)) (square (- b2 b1)))))))

;;; Identifier:
;;;   MAX-IRGB-DISTANCE
;;; Type:
;;;   real number
;;; Value:
;;;   The largest distance between any two RGB colors
(define MAX-IRGB-DISTANCE (irgb-distance (irgb 0 0 0) (irgb 255 255 255)))

e. Write a procedure, (sample-and-replace-e! image left top width height), that randomly samples at least three pixels from the given area and chooses between at least three different ways to recolor the area based on criteria you choose.