;;; Problem 1-3. Turtle Graphics

;;; The book "Thinking about TLC Logo" by Allen, Davis, and Johnson
;;; is highly recommended as further reading on the subject
;;; of turtle graphics and most of the ideas discussed in the
;;; course.  The many delightful cartoons alone make it worth
;;; reading. 

;;; Obtain the definitions of the turtle graphics primitives.
 
(load "{turing}<3-lisp.utilities>turtle.3-lisp")

;;; (WIPE) clears the window and "homes" the turtle.

(define WIPE (lambda [] (begin (clearwindow) (center))))

; In general, all drawings shown below were drawn just
; after a (WIPE), and no attempt was made to drag the 
; turtle off the picture it had just traced out.


;;; 1-3-a  Triangles and rectangles.

;;; (TRIANGLE s) will draw an equilateral triangle S units
;;; on a side.  The turtle is returned to where it started.

(define TRIANGLE
   (lambda [size]
      (begin (forward size)
             (right 120)
             (forward size)
             (right 120)
             (forward size)
             (right 120))))

; Example triangle:

      (triangle 150)


;;; (RECTANGLE l w) will draw rectangle L units long and W units
;;; wide.  The turtle is returned to where it started.


(define RECTANGLE
   (lambda [length width]
      (begin (forward length)
             (right 90)
             (forward width)
             (right 90)
             (forward length)
             (right 90)
             (forward width)
             (right 90))))

; Example rectangle:
 
      (rectangle 150 70)



;;; 1-3-b  Polygons. 

;;; (POLYGON n s) will draw an N-sided (N>=3) regular polygon with sides
;;; s units long.  As always, the turtle is returned to where it
;;; started.    


(define POLYGON
   (lambda [sides size]
      (for i 1 sides
         (begin (forward size)
                (right (/ 360 sides))))))

; Example polygons:

       (polygon 5 75) 

       (polygon 8 60)



; Nested polygons, for the fun of it.

       (for sides 3 13 (polygon sides 30))



;;; 1-3-c  A setup procedure.

;;; (SETUP x y angle erase?) positions the turtle at the given
;;; X and Y coordinates, facing in a particular direction, and
;;; optionally clears the screen if ERASE? => $TRUE.

;;; The coordinate system is as follows:
;;; Coordinate 0,0 corresponds to the center of the
;;; turtle's window, with the x axis running horizontally, and 
;;; the y axis running vertically.  The units are in screen pixels;
;;; i.e., roughly 75 units to the inch.  The base orientation is
;;; facing east (along the x axis, towards +infinity); ANGLE 
;;; indicates the desired clockwise deviation, in degrees,
;;; from the base orientation.

(define SETUP
   (lambda [x y angle erase?]
      (begin
         (center)
         (jump x)
         (left 90)
         (jump y)
         (right 90)
         (left angle)
         (if erase? (clearwindow)))))

; Examples:

(setup 0 0 0 $true)  ; Face left
(setup 0 0 90 $true)  ; Face up
(setup 0 0 180 $true)  ; Face right
(setup 0 0 90 $true)  ; Face down


;;; (4-CORNERS n s x y) uses SETUP to draw (POLYGON n s)
;;; in each of the four corners of the screen, where X and Y
;;; give the maximum x and y coordinates of the screen, resp.
;;; Frankly, this procedure is quite shoddy with regard to
;;; the positioning of the polygons; a much better solution
;;; could have been written if POLYGON drew a polygon *centered*
;;; at the starting point, instead of tangential to the staring
;;; point.    

(define 4-corners
   (lambda [sides size max-x max-y]
      (begin
         (setup max-x max-y 90 $true)
         (polygon sides size)
         (setup max-x (- 0 max-y) 90 $false)
         (polygon sides size)
         (setup (- 0 max-x) max-y 90 $false)
         (polygon sides size)
         (setup (- 0 max-x) (- 0 max-y) 90 $false)
         (polygon sides size)
         (setup 0 0 0 $false))))

; Example of pentagons in the corners (well...) of a 100 by 60
; screen:

     (4-corners 5 40 100 60)



;;; 1-3-d Recursive polygons.

;;; (RECURSIVE-POLYGON n s k) will draw a K-th generation
;;; N-sided recursive polygon S units a side.  The 0th
;;; generation recursive polygon (any size, any number of 
;;; sides) is defined to be a point.  The first generation is
;;; simply the appropriately-sized regular N-sided polygon.
;;; A K>1 recursive polygon is a regular N-sided polygon with
;;; smaller, simpler recursive polygons at each vertex.

(let [[scale-down-factor 3]]
  (define RECURSIVE-POLYGONS
     (lambda [sides size generation]
        (if (<= generation 0)
            "ok"
            (for i 1 sides
               (begin
                  (forward size)
                  (recursive-polygons sides
                     (/ size scale-down-factor)
                     (1- generation))
                  (right (/ 360 sides)) ))))))

; Note: The three expressions within the scope of the FOR
; can be permuted without violating the spirit of the
; procedure.
 
; Examples:

      (recursive-polygons 3 100 3)
      (recursive-polygons 4 100 3)
      (recursive-polygons 6 75 3)



;;; 1-3-e  Thick polygons.

(load "{turing}<3-lisp.utilities>ps-1-definitions.3-lisp")

; In the interest of time and space, we have not show commented
; versions of SPIRAL, WHORL, BORDER, etc. 

; But for those who didn't get to the stage to running the
; suggested test:

     (begin (setup -200 100 0 $true)
            (rectangle 8 5 16 3 right))


;;; (THICK-POLYGON n s nib-size) draws an N-sided regular polygon
;;; with sides S units long using lines NIB-SIZE units wide.

(define THICK-POLYGON ; c.f. POLYGON
   (lambda [sides size nib-size]                   
      (let [[forward (thick-forward nib-size)]]
         (for i 1 sides
            (begin (forward size)
                   (right (/ 360 sides)))))))

; Example of a thick pentagon:

     (thick-polygon 5 100 10)



; The trick of rebinding FORWARD can be made in the
; definitions of BORDER, etc. to yield THICK-BORDER,etc.


;;; 1-3-f   STRING-MAP and ASSOC

;;; (ASSOC key seq) looks up KEY in SEQ and returns the
;;; item paired with it.  SEQ must designate
;;; a sequence of two element sequences of the form [key x].
;;; In keeping with LISP tradition, ASSOC will return $FALSE
;;; if KEY is not present.

(define ASSOC
   (lambda [key seq]
      (cond [(null seq) $false]
            [(= key (first (first seq))) (second (first seq))]
            [$true (assoc key (rest seq))])))

; Examples:

    1> (assoc 2 [[1 "a"] [2 "b"] [3 "c"]])
    1= "b"
    1> (assoc 10 [[1 "a"] [2 "b"] [3 "c"]])
    1= $false
    1> (assoc 2 [[2 "a"] [2 "b"] [2 "c"]])
    1= "a"

;;; (STRING-TO-SEQUENCE-OF-CHARACTERS string) designates the
;;; sequence of characters in STRING.  This function will be
;;; employed in the definition of STRING-MAP.

(define STRING-TO-SEQUENCE-OF-CHARACTERS
   (lambda [string]
      (if (zero (string-length string))
          []
          (cons (nth-char 1 string)
                (string-to-sequence-of-characters 
                    (substring 2 (string-length string) string))))))

; Examples:

    1> (string-to-sequence-of-characters "csli")
    1= [#c #s #l #i]
    1> (string-to-sequence-of-characters "")
    1= []
    1> (string-to-sequence-of-characters "G ap")
    1= [#G #  #a #p]


;;; (STRING-MAP f string) applys the function designated by F
;;; to each character in the string STRING, collecting the results
;;; into a sequence.  Since STRING-MAP is intended to be used with
;;; procedures F with side-effects, it is also important to know
;;; that the characters are visited in left-to-right order.

(define STRING-MAP
   (lambda [fun string]
      (map fun (string-to-sequence-of-characters string))))

; Example:

    1> (string-map list "abc")
    1= [[#a][#b][#c]]


;;; 1-3-g Large letters.

;;; Each letter drawing procedure assumes that the initial
;;; position of the turtle is at the lower left corner of the
;;; region in which the character is drawn, facing to the right.
;;; After drawing the large letter, the turtle is left on the
;;; bottom right side of letter just drawn, again facing to the
;;; right.  These simple assumptions allow words to be composed
;;; in normal left-to-right order without additional concern about
;;; inter-letter spacing, basline alignment.

; Note the error on p.15 of the problem set:  ((DRAW/Q 4)) in 
; "... the expression ((DRAW/Q 4)) would draw a 'Q' four pixels thick."
; should be (((DRAW/Q (THICK-FORWARD 4))).  

(define DRAW/C
   (lambda [forward]
      (lambda []
         (begin
            (jump 5)
            (forward 75)
            (jump -75)
            (left 90)
            (forward 100)
            (right 90)
            (forward 75)
            (right 90)
            (jump 100)
            (left 90)
            (jump 5)))))

; Sample "C".

   (begin (wipe) ((draw/c (thick-forward 5))))


(define DRAW/I
   (lambda [forward]
      (lambda []
         (begin
            (jump 5)
            (left 90)
            (forward 100)
            (jump -100)
            (right 90)
            (jump 5)))))

; Sample "I".

   (begin (wipe) ((draw/i (thick-forward 5))))


(define DRAW/L
   (lambda [forward]
      (lambda []
         (begin
            (jump 5)
            (left 90)
            (forward 100)
            (jump -100)
            (right 90)
            (forward 75)
            (jump 5)))))

; Sample "L".

   (begin (wipe) ((draw/l (thick-forward 5))))


(define DRAW/S
   (lambda [forward]
      (lambda []
         (begin
            (jump 5)
            (forward 75)
            (left 90)
            (forward 30)
            (left 60)
            (forward 80)
            (right 60)
            (forward 30)
            (right 90)
            (forward 75)
            (right 90)
            (jump 100)
            (left 90)
            (jump 5)))))


; Sample "S".

   (begin (wipe) ((draw/s (thick-forward 5))))


;;; *DRAWING-TABLE* designates a sequence of character
;;; procedure pairings (suitable for use with ASSOC).

(set *DRAWING-TABLE* 
     [[#C draw/C] [#I draw/I] [#L draw/L] [#S draw/S]])


; Use TURTLE-WRITE to spell out "CSLI".  (Oops! No border.)

     (begin (setup -170 0 0 $true)
            (turtle-write "CSLI" 10))


;;; End of solution to 1-3.