CS2322

Laboratory Problem 25

The Square-Limit Language

Extra Credit Problem

Due Before End of Classes

We describe an implementation of a variation of Peter Henderson's Escher Square-Limit language.

The Square-Limit Language is a simple graphics language developed by Peter Henderson, of Oxford University. The Square-Limit language was based on ideas of Dutch artist M. C. Escher. One of the key features of the implementation is that it represents pictures as procedures. This is an example of object oriented programming, that is, a picture object packages both the data structure of the picture as well as procedures (methods) for its manipulation and display.

In particular, a picture is defined to be a procedure that takes a rectangle as input and causes something to be drawn, scaled to fit in the rectangle. A rectangle will be represented by three vectors: an origin, a horizontal vector, and a vertical vector. The origin o will be represented as a vector whose coordinates give the coordinates of the origin with respect to some external coordinate system, such as the graphics screen coordinates. The horizontal vector (call it h) and the vertical vector (call it v) give the offsets of the sides of the rectangle from the origin . As a consequence, any rectangle defines a linear transformation coordinate map by mapping the point (1,0) in its coordinate frame into the point specified by the end of the horizontal vector, offset from the origin, and by mapping the point (0,1) in its coordinate frame into the point specified by the end of the vertical vector, offset from the origin.

In algebraic terms, a general point (x , y) gets mapped to the vector

o + x * h + y * v

where remember that o, h, and v are vectors, and x and y are scalars.

We represent a rectangle by a list of three vectors: the location of the origin of the rectangle, the vector from that origin to the end of the horizontal axis, and the vector to the end of the vertical axis. Here are the constructors and accessors for rectangles.

(define make-rect list)
(define origin car)
(define horiz cadr)
(define vert caddr)

Using this map, points with coordinates between 0 and 1 end up inside the rectangle. Here is a procedure that takes a rectangle as input and returns the corresponding coordinate transformation, which is itself a procedure:

(define (coord-map rect)

(lambda (point)

(+vect

(+vect (scale (xcor point)

(horiz rect))

(scale (ycor point)

(vert rect)))

(origin rect))))

The contents of a picture are defined by a collection of line segments. Segments are represented as pairs of points, and points (vectors) are represented as pairs of numbers. Here are the constructors and accessors for vectors and segments.

(define make-vect cons)

(define xcor car)

(define ycor cdr)

(define make-segment cons)

(define seg-start car)

(define seg-end cdr)

Given a rectangle as input, the picture draws the image of each line segment inside the rectangle. Here is a procedure that constructs the picture, given a list of segments. The constuctor for a picture uses the mapping function for-each which applies its function argument to each line segment in a rectangle for its side effect of drawing that line segment after first changing its points to the coordinate system of the rectangle.

(define (make-picture seglist)

(lambda (rect)

(for-each

(lambda (segment)

(drawline ((coord-map rect) (seg-start segment))

((coord-map rect) (seg-end segment))))

seglist)))

(define empty-picture (make-picture '()))

Rotation

To rotate a picture 90 degrees counterclockwise, we need only draw the picture with respect to a new rectangle:

(define (rotate90 pict)

(lambda (rect)

(pict (make-rect

(+vect (origin rect)

(horiz rect))

(vert rect)

(scale -1 (horiz rect))))))

(define (repeated function n)

(lambda (thing)

(if (= n 0)

thing

((repeated function (-1+ n)) (function thing)))))

(define rotate180 (repeated rotate90 2))

(define rotate270 (repeated rotate90 3))

Horizontal flip is also drawing with respect to a new rectangle:

(define (flip pict)

(lambda (rect)

(pict (make-rect (+vect (origin rect) (horiz rect))

(scale -1 (horiz rect))

(vert rect)))))

Means of combining pictures

Since pictures are specified by procedures, we can create higher order procedures that combine simple pictures together in various ways. For example, the together operation takes two pictures and combines them into a single picture, lying superimposed.

(define (together pict1 pict2)

(lambda (rect)

(pict1 rect)

(pict2 rect)))

The beside operation takes two pictures and scales them according to some relative width to fit in a single rectangle. Beside takes as input, the two pictures, plus a variable a which specifies the percentage of horizontal devoted to the first picture.

(define (beside pict1 pict2 a)

(lambda (rect)

(pict1 (make-rect

(origin rect)

(scale a (horiz rect))

(vert rect)))

(pict2 (make-rect

(+vect (origin rect)

(scale a (horiz rect)))

(scale (- 1 a) (horiz rect))

(vert rect)))))

above is defined in terms of beside

(define (above pict1 pict2 a)

(rotate270 (beside (rotate90 pict1)

(rotate90 pict2)

a)))

Here are some operations defined in terms of the procedures above and beside:

(define (4pict pict1 rot1 pict2 rot2 pict3 rot3 pict4 rot4)

(beside (above ((repeated rotate90 rot1) pict1)

((repeated rotate90 rot2) pict2)

.5)

(above ((repeated rotate90 rot3) pict3)

((repeated rotate90 rot4) pict4)

.5)

.5))

(define (4same pict rot1 rot2 rot3 rot4)

(4pict pict rot1 pict rot2 pict rot3 pict rot4))

(define (up-push pict n)

(if (= n 0)

pict

(above (up-push pict (-1+ n))

pict

.25)))

(define (right-push pict n)

(if (= n 0)

pict

(beside pict

(right-push pict (-1+ n))

.75)))

(define (corner-push pict n)

(if (= n 0)

pict

(above (beside (up-push pict n)

(corner-push pict (-1+ n))

.75)

(beside pict

(right-push pict (-1+ n))

.75)

.25)))

(define (square-limit pict n)

(4same (corner-push pict n) 1 2 0 3))

Rectangles and primitive pictures

The rectangle that defines the screen is

(define screen (make-rect (make-vect west south)

(make-vect (- east west) 0)

(make-vect 0 (- north south))))

A useful test picture is one that draws the outline of its given rectangle:

(define outline-picture
(let ((v1 (make-vect 0 0))
(v2 (make-vect 1 0))
(v3 (make-vect 1 1))
(v4 (make-vect 0 1))
(make-picture (list (make-segment v1 v2)
(make-segment v2 v3)
(make-segment v3 v4)
(make-segment v4 v1)))))

With the above, one could, for example, cause the outline rectangle to be drawn on the full (square) screen by:

(outline-picture screen)

The following procedure, which applies its argument to the screen after clearing the graphics screen, will save some typing.

(define (draw pict)

(clear-graphics)

(pict screen))

The definitions below present a few simple (and not very interesting) patterns. To gain a better understanding of the square-limit language, you can play with them, using various combinations of the operators, and define new primitives.

(define wedge (make-picture
(list (make-segment (make-vect 0 0)
(make-vect .5 .5))
(make-segment (make-vect .5 .5)
(make-vect 1 0)))))

(define line (make-picture
(list (make-segment (make-vect 0 .5)
(make-vect 1 .5)))))

(define star (make-picture
(list (make-segment (make-vect 0 .5)
(make-vect .5 0))
(make-segment (make-vect .5 0)
(make-vect 1 .5))
(make-segment (make-vect 1 .5)
(make-vect .5 1))
(make-segment (make-vect .5 1)
(make-vect 0 .5)))))

Define procedures which use the square limit functions to produce some interesting pictures.

Lab 25 Scheme Code