Plumbing Graphics

Philosophy

We use plumbing diagrams as a data structure for doing graphics. A plumbing diagram is, conceptually, a bunch of pipes stuck together.

There are functions for creating little pieces of pipe (primitive plumbing diagrams) and there are functions for putting a small plumbing diagrams together to form big plumbing diagrams.

This situation is exactly analogous to the situation with numbers, where we can make little numbers, and we have functions to put little numbers together to form bigger numbers.

When we calculate with numbers, intermediate results are not displayed. Only when the calculation is finished and the ultimate result is know is that ultimate result printed. Similarly, the intermediate plumbing diagrams are not displayed - rather, only the final result is displayed.

Primitives

Invoking (straight 100) creates a straight piece of pipe, 100 pixels long. Typing it to the prompt results in the following interaction, and the indicated diagram appears in the graphics window.
Welcome to MzScheme ... actually MrEd with its editor disabled
 CS257 Plumbing Graphics 3.0 loaded
 CS257 Simply Scheme extensions loaded.
> (straight 100)
#<graphic>

Calling (bend 45) creates a tiny piece of pipe with a 45 degree clockwise bend in it.

> (bend 45)

Invoking (transparent 100) creates a straight but transparent piece of pipe 100 pixels long.

> (transparent 100)

Constructors

The most important plumbing diagram constructor is adjoin. You pass adjoin any number of plumbing diagrams, and it hooks them together, in left to right order, to make a longer plumbing diagram. The end of each diagram (shown by a red arrow when the diagram is displayed) is fastened to the beginning of the next one (shown by the blue arrow.)

Here is an example

> (straight 100)
> (adjoin (straight 100) (transparent 50) (straight 100))
> (adjoin (straight 100) (bend 45) (straight 50))

There are a few other constructors, but they are used relatively infrequently. The adorn function takes its first argument, and glues the start of each remaining object onto the end of this first object. The resulting object's end is the end of the first argument.

> (adorn (straight 100) (straight 50))
> (adorn (straight 100) (adjoin (bend -90) (straight 50))
                        (adjoin (bend 45) (straight 100))
                        (adjoin (bend 120) (straight 50)))

Using plumbing graphics

There are only a few primitive arithmetic operations, but they can be combined to form complicated expressions of immense beauty and utility. Similarly, the few graphics functions described above can be used to build complex structures which are of interest to both the artist and the mathematician.
(define rep4 (lambda (g) (adjoin g g g g)))

(rep4 (adjoin (straight 100) (bend 90)))
(rep4 (adjoin (rep4 (adjoin (straight 100) (bend 90)))
              (transparent 110)))
(define rep5 (lambda (g) (adjoin g g g g g)))

(rep5 (adjoin (straight (/ 500 5)) (bend (/ 360 5))))

Repetition

(define rep
  (lambda (n g)
    (cond ((= n 0) (adjoin))
	  ((= n 1) g)
	  ((= n 2) (adjoin g g))
	  ((= n 3) (adjoin g g g))
	  ((= n 4) (adjoin g g g g))
	  ((= n 5) (adjoin g g g g g))
	  ((= n 6) (adjoin g g g g g g))
	  ((= n 7) (adjoin g g g g g g g))
	  ((= n 8) (adjoin g g g g g g g g))
	  ((= n 9) (adjoin g g g g g g g g g)))))

(define poly
  (lambda (n)
    (rep n (adjoin (straight (/ 500 n))
		   (bend (/ 360 n))))))

(poly 3)
(poly 7)
(define rep
  (lambda (n g)
    (cond ((= n 0) (adjoin))
	  ((= n 1) (adjoin g (rep 0 g)))
	  ((= n 2) (adjoin g (rep 1 g)))
	  ((= n 3) (adjoin g (rep 2 g)))
	  ((= n 4) (adjoin g (rep 3 g)))
	  ((= n 5) (adjoin g (rep 4 g)))
	  ((= n 6) (adjoin g (rep 5 g)))
	  ((= n 7) (adjoin g (rep 6 g)))
	  ((= n 8) (adjoin g (rep 7 g)))
	  ((= n 9) (adjoin g (rep 8 g))))))

(poly 6)

Repetition and Recursion

(define rep
  (lambda (n g)
    (cond ((= n 0) (adjoin))
	  ((> n 0) (adjoin g (rep (- n 1) g))))))

(poly 50)

Trees

(define tree-step
  (lambda (trunk left-branch right-branch)
    (adorn trunk
           (adjoin (bend -20) left-branch)
	   (adjoin (bend 20) right-branch))))

(define tree1 (lambda () (straight 20)))
(define tree2 (lambda () (tree-step (straight 30) (tree1) (tree1))))
(define tree3 (lambda () (tree-step (straight 40) (tree2) (tree2))))
(define tree4 (lambda () (tree-step (straight 50) (tree3) (tree3))))

(tree4)

Trees and Recursion

(define tree
  (lambda (n)
    (cond ((= n 1) (straight 20))
	  ((> n 1) (tree-step (straight (+ 10 (* n 10)))
			      (tree (- n 1))
			      (tree (- n 1)))))))

(tree 8)

Random Rustling Wind

Add some randomness to the branching angles:
(define tree-step
  (lambda (trunk left-branch right-branch)
    (adorn trunk
           (adjoin (bend (+ (random 20) -30))
		   left-branch)
	   (adjoin (bend (+ (random 20) 10))
		   right-branch))))

(tree 8)
Add some randomness to the branch lengths as well:
(define tree
  (lambda (n)
    (cond ((= n 1) (straight 20))
	  ((> n 1) (tree-step (straight (+ 10 (* n (+ 5 (random 5)))))
			      (tree (- n 1))
			      (tree (- n 1)))))))

(tree 9)

The Koch Curve

(define koch-step
  (lambda (side angle)
    (adjoin side
	    (bend (- angle))
	    side
	    (bend (* 2 angle))
	    side
	    (bend (- angle))
	    side)))

(koch-step (straight 50) 60)
(define koch
  (lambda (n len angle)
    (if (= n 0)
	(straight len)
	(koch-step (koch (- n 1) len angle)
		   angle))))

(koch 2 50 60)
(koch 5 1.5 60)
(koch 5 6 80)
(koch 7 1 80)
(rep 3 (adjoin (koch 5 1.5 60) (bend 120)))

Abbreviations

To simplify typing, some abbreviations have been defined.
function nicknames
straight st, move
bend be, turn
adjoin adj

Don't do this at home kids!

Well, actually you are encouraged to do this at home. See the Scheme at Home section of the class resources page for details.
Barak Pearlmutter <barak@pearlmutter.net>