## Plotter

### April 11, 2014

Our program is simple, but tedious. It first reads the specification (all the rules are required, but they may appear in any order) and the data, checks that all the specifications have been given, then plots the data by writing points to a matrix of characters, and finally writes the graph to the output. We present the complete program without further comment:

`(define height 24) ; number of printable rows`

(define width 80) ; number of printable columns

(define ox 10) ; x-origin, leave room for y-ticks

(define oy 1) ; y-origin, leave room for x-ticks

```
```(define label #f) ; graph label on top row

(define y-lo #f) ; lo end of y-axis

(define y-hi #f) ; hi end of y-axis

(define x-lo #f) ; lo end of x-axis

(define x-hi #f) ; hi end of x-axis

(define y-ticks (list)) ; list of y-axis tick marks

(define x-ticks (list)) ; list of x-axis tick marks

(define data (list)) ; list of x/y points to plot

(define grid (make-matrix width height #\space))

(define (graph file-name)

(set! label #f) (set! y-lo #f) (set! y-hi #f) (set! x-lo #f) (set! x-hi #f)

(set! y-ticks (list)) (set! x-ticks (list)) (set! data (list))

(set! grid (make-matrix width height #\space))

(with-input-from-file file-name (lambda ()

(let loop ((line (read-line)))

(if (eof-object? line) (plot)

(let ((fields (string-split #\space line)))

(cond ((null? fields) (verify-parameters))

((null? (cddr fields))

(set! data (cons (cons (string->number (car fields))

(string->number (cadr fields))) data)))

((string=? (car fields) "label")

(set! label (substring line

(+ (string-length "label") 1)

(string-length line))))

((and (string=? (car fields) "left")

(string=? (cadr fields) "range"))

(set! y-lo (string->number (caddr fields)))

(set! y-hi (string->number (cadddr fields))))

((and (string=? (car fields) "bottom")

(string=? (cadr fields) "range"))

(set! x-lo (string->number (caddr fields)))

(set! x-hi (string->number (cadddr fields))))

((and (string=? (car fields) "left")

(string=? (cadr fields) "ticks"))

(set! y-ticks (map string->number (cddr fields))))

((and (string=? (car fields) "bottom")

(string=? (cadr fields) "ticks"))

(set! x-ticks (map string->number (cddr fields))))

(else (error 'graph "unrecognized input")))

(loop (read-line))))))))

(define (verify-parameters)

(when (not (and label y-lo y-hi x-lo x-hi y-ticks x-ticks))

(error 'verify-parameters "missing parameters"))

(when (not (< y-lo y-hi))

(error 'verify-parameters "invalid left range"))

(when (not (< x-lo x-hi))

(error 'verify-parameters "invalid bottom range"))

(when (not (apply < y-ticks))

(error 'verify-parameters "invalid left ticks"))

(when (not (apply < x-ticks))

(error 'verify-parameters "invalid bottom ticks")

(define (plot)

(frame) (ticks) (labels) (points) (draw))

(define (frame)

(for (i ox width)

(matrix-set! grid i oy #\-)

(matrix-set! grid i (- height 2) #\-))

(for (i oy (- height 1))

(matrix-set! grid ox i #\|)

(matrix-set! grid (- width 1) i #\|)))

(define (ticks)

(do ((ts y-ticks (cdr ts))) ((null? ts))

(matrix-set! grid ox (y-scale (car ts)) #\-)

(display-at (y-scale (car ts)) (- ox 1 (string-length (number->string (car ts))))

(number->string (car ts))))

(do ((ts x-ticks (cdr ts))) ((null? ts))

(matrix-set! grid (x-scale (car ts)) oy #\|)

(display-at (- oy 1) (- (x-scale (car ts))

(quotient (string-length (number->string (car ts))) 2))

(number->string (car ts)))))

(define (labels)

(display-at (- height oy) (quotient (- width (string-length label)) 2) label))

(define (points)

(do ((ds data (cdr ds))) ((null? ds))

(matrix-set! grid (x-scale (caar ds)) (y-scale (cdar ds)) #\*)))

(define (x-scale x)

(round (+ (* (/ (- x x-lo) (- x-hi x-lo)) (- width 1 ox)) ox)))

(define (y-scale y)

(round (+ (* (/ (- y y-lo) (- y-hi y-lo)) (- height 3 oy)) oy)))

(define (display-at r c str)

(for (i 0 (string-length str))

(matrix-set! grid (+ i c) r (string-ref str i))))

`(define (draw)`

(for (r (- height 1) -1 -1)

(for (c 0 width)

(display (matrix-ref grid c r)))

(newline)))

Assuming the data file from the previous page is in a file called `AnnualTrafficDeaths`

, produce output like this:

> (graph "AnnualTrafficDeaths") Annual Traffic Deaths, USA, 1899-2012 |--------------------------------------------------------------------| | * * | | **** ** | | *** | 45000 - ** ** *** | | * *** * ******* | | * ** * | | * * ****** * | | * * ** * * * * | 30000 - *** * *** * | | * * * * | | * | | * ** | | ** | 15000 - * | | * | | ** | | ** | | ** | 0 - ******** | | | |-----|----------|----------|----------|----------|----------|-------| 1905 1925 1945 1965 1985 2005

We used matrices, `read-line`

, and `string-split`

from the Standard Prelude. You can run the program at http://programmingpraxis.codepad.org/SvXJRh3u.