The SICP Picture Language In Racket
Get to the picture language section of SICP and actually want to draw stuff? Find out how below.
About a month ago I decided to start taking my programming more seriously. I realized that I had done very little recently to develop my craft, and so I set out to find some resources to whip my mind back into shape. Since it had been a while I decided it might be nice to start from the basics – even though I feel I already know them – and work my way back up. One of the books I’ve always wanted to work my way through has been The Structure and Interpretation of Computer Programs (usually abbreviated SICP). Widely considered a classic text in the computer science canon. So I set up a learning program for myself and I’ve finally been working my way through it at a steady pace. About a month in I can say, without a doubt, it is one of the most fun and rewarding things I’ve decided to do with my free-time.
To begin, I created a learning regimen for myself. I spend at least an hour each night reading the book and/or working through the exercises. I have to complete every exercise along the way, no skipping. Using this more disciplined approach has helped me tremendously. It has deepened my understanding of Lisp and helped develop my intuition for it as a language.
One of the most enjoyable parts of the book so far has been the picture language. I’m a sucker for visual feedback when it comes to learning, and I have probably invested more time in this tiny section than anywhere else in the book. The brilliance of the picture language is that it shows how abstractions can be layered to produce a powerful system for manipulating pictures. The image above was generated by the following line of code:
((square-limit wave-painter 4) unit-frame)
The picture language begins as a description of vectors in the plane. Those points are quickly abstracted to line segments and frames. The painter concept is then introduced – a painter, given a frame, draws itself inside that frame. From these simple pieces vectors, frames, and painters we can produce any image pattern we like.
Actually Drawing Pictures With The Picture Language
One of the big holes in this section is that it doesn’t contain any code for actually drawing things. It was in pursuit of this that I eventually decided to use the Racket language to start getting real visual feedback. First, you’ll need all the code for making vectors and frames:
(define (make-vect x y) (cons x y)) (define (xcor-vect v) (car v)) (define (ycor-vect v) (cdr v)) (define (add-vect v1 v2) (make-vect (+ (xcor-vect v1) (xcor-vect v2)) (+ (ycor-vect v1) (ycor-vect v2)))) (define (sub-vect v1 v2) (make-vect (- (xcor-vect v1) (xcor-vect v2)) (- (ycor-vect v1) (ycor-vect v2)))) (define (scale-vect s v) (make-vect (* s (xcor-vect v)) (* s (ycor-vect v)))) (define (make-frame origin edge1 edge2) (list origin edge1 edge2)) (define (origin-frame frame) (car frame)) (define (edge1-frame frame) (cadr frame)) (define (edge2-frame frame) (caddr frame)) (define (frame-coord-map frame) (lambda (v) (add-vect (origin-frame frame) (add-vect (scale-vect (xcor-vect v) (edge1-frame frame)) (scale-vect (ycor-vect v) (edge2-frame frame))))))
From here, getting graphics set up is pretty easy. The basic setup for the picture language is listed below:
#lang racket/gui (require graphics/graphics) (open-graphics) (define vp (open-viewport "A Picture Language" 500 500)) (define draw (draw-viewport vp)) (define (clear) ((clear-viewport vp))) (define line (draw-line vp)) (define (segments->painter segment-list) (lambda (frame) (for-each (lambda (segment) (let ((start-coord-map ((frame-coord-map frame) (start-segment segment))) (end-coord-map ((frame-coord-map frame) (end-segment segment)))) (line (make-posn (xcor-vect start-coord-map) (ycor-vect start-coord-map)) (make-posn (xcor-vect end-coord-map) (ycor-vect end-coord-map))))) segment-list))) (define x-painter (segments->painter (list (make-segment (make-vect 0 0) (make-vect 1 1)) (make-segment (make-vect 0 1) (make-vect 1 0))))) (define unit-frame (make-frame (make-vect 0 500) (make-vect 500 0) (make-vect 0 -500))) (x-painter unit-frame)
If all went well you should see something like the following in a new window:
You can checkout the gist with the complete code here. Have fun and draw cool stuff!