# Geometric Art

Summary: In this laboratory, you will further explore the techniques for making geometric images.

## Preparation

a. Create a new 200x200 image and call it `canvas`.

b. If you have not already done so, add the procedures from the reading to your library. You can find the code for these procedures at the end of this lab.

## Exercises

### Exercise 1: Exploring Examples

a. Test each of the two basic procedures from the reading: `draw-three-parallel-lines!` and `draw-circle!`.

b. Look at each of the examples in the Introduction to the corresponding reading, predict what you think the example code will do, and then run the example in DrFu.

c. The reading provides four procedures that draw parallel lines (with some variation between them), `draw-parallel-lines!`, `draw-colored-parallel-lines!`, `draw-sin-with-parallel-lines!`, and `draw-parallel-lines-with-decreasing-spacing!`. Make sure you understand what each of the procedures is supposed to do, and then try using each with two different sets of parameters.

### Exercise 2: Drawing Functions, Revisited

a. Make a copy of `draw-sin-with-parallel-lines!` and rename it `draw-func-with-parallel-lines!`. Make sure that this renamed copy still works.

b. Revise the procedure to graph cosine, rather than sine.

c. Revise the procedure to graph the sum of sine and cosine, rather than the sine (or cosine).

d. Revise the procedure to graph a function of your choice.

e. Revise the function so that it draws each line in a different color.

### Exercise 3: Drawing Parallel Lines with Different Brushes

In the reading, we considered how one might use different brushes for neighboring parallel lines, but never finished the procedure to do that. Here's a header for that procedure.

```(define draw-parallel-lines-with-many-brushes!
(lambda (image brushes n start-col start-row end-col end-row hoff voff)
...))```

a. Finish writing the procedure. You should look at the definition of some of the other parallel line procedures (such as `draw-parallel-lines!`) to figure out what belongs.

b. What do you expect the following call to generate?

````>` ```(define some-brushes
(list "Circle (01)" "Circle (03)" "Circle (05)"
"Circle (07)" "Circle (09)" "Circle (11)"))```
`>` ```(draw-parallel-lines-with-many-brushes!
canvas some-brushes 12
10 10 100 10
0 15)```
```

d. Here's a slight variation of the previous set of instructions in which we use a different list of brushes. (We also change the orientation of the lines.) What effect do you expect this change to the brushes to have?

````>` ```(define more-brushes
(append some-brushes (cdr (reverse (cdr some-brushes)))))```
`>` ```(draw-parallel-lines-with-many-brushes!
canvas more-brushes 12
10 10 10 100
15 0)```
```

f. Extend the procedure so that it varies both color and brushes.

### Exercise 4: Decreasing Spacing, Revisited

In the reading, we explored a procedure in which each space between lines is half of the space between lines to the left. Some callers might prefer to have a different ratio, such as 3/4 or 1/3. Let's rewrite the procedure to permit the caller to specify that parameter, which we'll call `ratio`. Here's the start of the new procedure, with a somewhat shorter name.

```(define draw-geometric-progression!
(lambda (image col start-row end-row spacing ratio close-enough)
...))```

a. Write that procedure.

b. What do you expect the following set of instructions to do?

````>` `(define new-canvas (image.new 200 200))`
`>` `(image.show new-canvas)`
`>` `(envt.set-brush! "Circle (01)")`
`>` `(draw-geometric-progression! canvas 5 10 190 80 0.5 2)`
`>` `(envt.update-displays!)`
```

d. Consider the following variant of the previous instructions, using a smaller ratio. How do you expect the generated image to differ?

````>` `(define new-canvas (image.new 200 200))`
`>` `(image.show new-canvas)`
`>` `(envt.set-brush! "Circle (01)")`
`>` `(draw-geometric-progression! canvas 5 10 190 80 0.4 2)`
`>` `(envt.update-displays!)`
```

f. Consider the following variant of the previous instructions, using a larger ratio. How do you expect the generated image to differ?

````>` `(define new-canvas (image.new 200 200))`
`>` `(image.show new-canvas)`
`>` `(envt.set-brush! "Circle (01)")`
`>` `(draw-geometric-progression! canvas 5 10 190 80 0.75 2)`
`>` `(envt.update-displays!)`
```

### Exercise 5: Decreasing Spacing, Re-Revisited

As you probably discovered in the final problem of the previous exercise, if the caller isn't careful, the final lines in the progression are drawn off the side of the image, which causes an error.

a. Update `draw-geometric-progression!` so that it stops for two reasons: the spacing is less than or equal to `close-enough` (as before) or the line to be drawn is outside the right-hand boundary of the image.

b. Rerun the final example from the previous exercise to confirm that you have corrected the error.

c. While `draw-geometric-progression!` is designed for progressions in which the spacing between subsequent lines decreases, we should also be able to make it draw progressions in which the spacing increases. Write instructions for generating a sequence of lines in which the first line appears in column 3, the next in column 5 (spacing of 2), the next in column 9 (spacing of 4), the next in column 17 (spacing of 8), and the spacing continues to double.

### Exercise 6: Concentric Circles

In the introduction to the corresponding reading, after drawing three parallel lines, we then generalized that technique. However, although we drew three concentric circles, we never generalized that technique.

a. Write a procedure, `(draw-bullseye! image n col row outer-radius delta-radius)`, that draws a sequence of `n` concentric circles, all with the same center, in which the outermost circle is radius of `outer-radius` and each subsequent circle has a radius that is `delta-radius` smaller.

b. As we saw in the reading, it can also be interesting to draw a sequence of circles that do not share a center, but have a progression of centers as well as a progression of smaller radii. Write a procedure, ```(draw-circle-sequence! image n col row radius d-col d-row d-radius)```, that draws a sequence of `n` circles, the first of which is centered at (`col`,`row`) with radius `radius`, and each subsequent one has the column offset by `d-col`, row offset by `d-row`, and radius offset by `d-radius`.

c. Rewrite `draw-bullseye!` so that its body is a call to `draw-circle-sequence!`.

## For Those With Extra Time

You need not do the extra problems in order. Read through them and decide which is most worth your time.

### Extra 1: Drawing Parallel Lines Without Explicit Recursion

In the corresponding reading, we wrote a procedure, `draw-parallel-lines!`, using direct recursion. It is also possible to write this procedure using a clever combination of `map`, `iota`, and an anonymous function. Try rewriting `draw-parallel-lines!` using this strategy.

### Extra 2: Sequences of Circles with Common Edges

Consider a procedure, `(draw-left-bounded-circles! image n col row radius delta-radius)`, that draws `n` circles, each of which has a left edge at column `col`, the outermost circle has radius `radius` and each subsequent circle has a radius that is `delta-radius` smaller than the previous circle.

a. Write this procedure using direct recursion.

b. Can you write this procedure with a call to `draw-circle-sequence!`? Why or why not?

### Extra 3: Sequences of Circles with Variation

Rewrite `draw-circle-sequence!` so that it adds some interesting variant. It might draw the circles in different colors, using a nonlinear offset of radii or centers, or anything else you deem appropriate.

## Explorations

### Exploration 1: Your Own Images

Create an image that you find aesthetically appealing with a few calls to one of the variants of `draw-parallel-lines!`. You should feel free to change brush and foreground color between calls.

### Exploration 2: Robert Delaunay

A number of artists have created interesting images using concentric circles and other geometric shapes. One of the more famous is French artist Robert Delaunay. Find a few of Delaunay's works and see if you can make something that resembles a portion of a work using a variant of `draw-circle-sequence!`.

## Useful Procedures

```;;; Procedure:
;;;   draw-circle!
;;; Parameters:
;;;   image, an image
;;;   col, an integer
;;;   row, an integer
;;; Purpose:
;;;   Draws a circle with the specified in the current brush and color, centered at (col,row).
;;; Produces:
;;;   [Nothing; Called for the side effect]
;;; Preconditions:
;;;   0 <= col < (image.width image)
;;;   0 <= row < (image.height image)
;;; Postconditions:
;;;   The image now contains the specified circle.  (The circle may not be visible.)
(define draw-circle!
(image.select-ellipse! image selection.replace
(image.stroke! image)
(image.select-nothing! image)))

;;; Procedure:
;;;   draw-three-parallel-lines!
;;; Parameters:
;;;   image, an image
;;;   start-col, an integer
;;;   start-row, an integer
;;;   end-col, an integer
;;;   end-row, an integer
;;;   hoffset, an integer
;;;   voffset, an integer
;;; Purpose:
;;;   Draw three parallel lines, with the first from (start-col,start-row)
;;;     to (end-col,end-row) and the starting point of the next two offset
;;;     horizontally by hoffset (and 2*hoffset) and vertically by voffset
;;;     (and 2*voffset).
;;; Produces:
;;;   [Nothing; Called for the side effect.]
;;; Preconditions:
;;;   All three parallel lines can be drawn on the image.
;;; Postcondtions:
;;;   The image has been appropriately modified.
(define draw-three-parallel-lines!
(lambda (image start-col start-row end-col end-row hoffset voffset)
(image.draw-line! image
start-col start-row
end-col end-row)
(image.draw-line! image
(+ hoffset start-col) (+ voffset start-row)
(+ hoffset end-col) (+ voffset end-row))
(image.draw-line! image
(+ (* 2 hoffset) start-col) (+ (* 2 voffset) start-row)
(+ (* 2 hoffset) end-col) (+ (* 2 voffset) end-row))))

;;; Procedure:
;;;   draw-parallel-lines!
;;; Parameters:
;;;   image, an image
;;;   n, an integer
;;;   start-col, an integer
;;;   start-row, an integer
;;;   end-col, an integer
;;;   end-row, an integer
;;;   hoffset, an integer
;;;   voffset, an integer
;;; Purpose:
;;;   Draw n parallel lines, with the first from (start-col,start-row)
;;;     to (end-col,end-row) and each subsequent one offset by the
;;;     appropriate multiple of hoffset and voffset.
;;; Produces:
;;;   [Nothing; Called for the side effect.]
;;; Preconditions:
;;;   All parallel lines can be drawn on the image.
;;; Postcondtions:
;;;   The image has been appropriately modified.
(define draw-parallel-lines!
(lambda (image n start-col start-row end-col end-row hoffset voffset)
(cond
((> n 1)
(image.draw-line! image
start-col start-row
end-col end-row)
(draw-parallel-lines! image
(- n 1)
(+ hoffset start-col) (+ voffset start-row)
(+ hoffset end-col) (+ voffset end-row)
hoffset voffset)))))

;;; Procedure:
;;;   position->color
;;; Parameters:
;;;   col, an integer
;;;   row, an integer
;;; Purpose:
;;;   Compute a color based on col and row.
;;; Produces:
;;;   color, an RGB color
;;; Preconditions:
;;; Postconditions:
;;;   Different col/row combinations are likely to give different colors.
;;;   Nearby col/row combinations may give similar colors.
(define position->color
(lambda (col row)
(rgb.new (+ 128 (* 128 (sin (* pi row 0.0625))))
(+ 128 (* 128 (cos (* pi col 0.0625))))
(+ 128 (* 128 (sin (* pi (+ row col) 0.0625)))))))

(define draw-colored-parallel-lines!
(lambda (image n start-col start-row end-col end-row hoff voff)
(cond
((> n 1)
(envt.set-fgcolor! (position->color start-col start-row))
(image.draw-line! image
start-col start-row
end-col end-row)
(draw-colored-parallel-lines! image
(- n 1)
(+ hoff start-col) (+ voff start-row)
(+ hoff end-col) (+ voff end-row)
hoff voff)))))

;;; Procedure:
;;;   draw-sin-with-parallel-lines!
;;; Parameters:
;;;   image, an image
;;;   n, an integer
;;;   offset, an integer
;;;   start-col, an integer
;;;   mid-row, an integer
;;; Purpose:
;;;   Draw a sequence of parallel lines, with the height of the
;;;   parallel line dependent on the column.
;;; Produces:
;;;   [Nothing, called for the side effect.]
(define draw-sin-with-parallel-lines!
(lambda (image n offset start-col mid-row)
(cond
((> n 0)
(image.draw-line! image
start-col mid-row
start-col (+ mid-row (* mid-row (sin (* n pi .01)))))
(draw-sin-with-parallel-lines!
image (- n 1) offset
(+ start-col offset) mid-row)))))

;;; Procedure:
;;;   draw-parallel-lines-with-decreasing-spacing!
;;; Parameters:
;;;   image, an image
;;;   col, an integer
;;;   start-row, an integer
;;;   end-row, an integer
;;;   spacing
;;;   close-enough
;;; Purpose:
;;;   Draw a sequence of vertical lines (each running from start-row
;;;   to end-row) starting at col, then spaced by spacing from col,
;;;   then by spacing/2 from that column, then spacing/4 from that
;;;   column, and so on and so forth until the distance between columns
;;;   is less than or equal to close-enough.
;;; Produces:
;;;   [Nothing. Called for the side effects.]
(define draw-parallel-lines-with-decreasing-spacing!
(lambda (image col start-row end-row spacing close-enough)
(image.draw-line! image col start-row col end-row)
(if (> spacing close-enough)
(draw-parallel-lines-with-decreasing-spacing!
image
(+ col spacing) start-row end-row
(/ spacing 2)
close-enough))))
```

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.