#lang racket
(require gigls/unsafe)

;;;Procedures:
;;;   image-series
;;; Parameters:
;;;   n, a natural number
;;;   width, a positive natural number
;;;   height, a positive natural number
;;; Purpose:
;;;   To create and show an interesting image which
;;;   in layer-1!, layer-2!, and layer-3! are
;;;   stacked on top of one another.
;;; Produces:
;;;   [Nothing; Called for the side effect.]
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   An image has been rendered.
;;;   (image-width image) = width
;;;   (image-height image) = height
;;;   layer-1! has been rendered on image.
;;;   layer-2! has been rendered on image on top of
;;;     the effect layer-1! created.
;;;   layer-3! has been rendered on image on top of
;;;     the effect layer-2! created.
;;; Acknowledgements:
;;;   This documentation is partially based off
;;;   of the documentation found in the Project Ideas readings
;;;   from Professor Weinman at:
;;;   http://www.cs.grinnell.edu/~weinman/courses/CSC151/2014F/readings/project-color-trees-reading.html
;;; Notes:
;;;   Particular inputs of n may be very computationally heavy.
;;;     This procedure calls for lots of turtles.
(define (image-series n width height)
  (display n)
  (let ([image (image-new width height)])
    (image-show image)
    (layer-1! image n)
    (layer-2! image n)))

;;; Procedures:
;;;   layer-1!
;;; Parameters:
;;;   image, an image
;;;   n, a natural number
;;; Purpose:
;;;   Render the first layer in image-series, consisting of
;;;   alternating rectangles and circles of various colors.
;;; Produces:
;;;   [Nothing; Called for the side effect.]
;;; Preconditions:
;;;   image is a valid image
;;; Postconditions:
;;;   The first layer of imager-series has been rendered.
;;;   rectangle and circle dimensions will be
;;;     (max 1 (/ (image-width image) 8)) by
;;;     (max 1 (/ (image-height image) 8)).
;;;   the arrangement of rectangles and circles
;;;     will by caused by pattern-vector-1, pattern-vector-2,
;;;     or manipulation of the shape-boolean value.
;;;   the rectangles and circles drawn will have colors taken
;;;     from the colors vector.
;;; Acknowledgements:
;;;   This documentation and layer-1! partially based off
;;;   of the procedures found in the Project Ideas readings
;;;   from Professor Weinman at:
;;;   http://www.cs.grinnell.edu/~weinman/courses/CSC151/2014F/readings/project-color-trees-reading.html
(define layer-1!
  (lambda (image n)
    (let* ([left 0]
           [top 0]
           [width (image-width image)]
           [height (image-height image)]
           [marker 0]
           [shape-boolean #t]
           [colors (vector (irgb 222 178 178) (irgb 222 204 178)
                           (irgb 214 222 178) (irgb 188 222 178)
                           (irgb 209 178 222) (irgb 222 178 214)
                           (irgb 222 178 194))] ;7 color variations
           [pattern-vector-1 (vector image-select-rectangle!)]
           [pattern-vector-2 (vector image-select-rectangle!
                                     image-select-rectangle!
                                     image-select-ellipse!
                                     image-select-rectangle!
                                     0)] ;3 pattern variations:
           ;two by pattern vectors and one via boolean variation with shape-boolean
           [vertical-endpoint-variations (list->vector (map (o increment increment) (iota 5)))]
           [vertical-endpoint-ref (/ 1 (vector-ref vertical-endpoint-variations (mod n 5)))]
           [minumum-shape-width (max 1 (/ (image-width image) 8))]
           [minumum-shape-height (max 1 (/ (image-height image) 8))])
      (let kernel ([left left]
                   [top top]
                   [kernel-width width]
                   [kernel-height height]
                   [marker marker]
                   [shape-boolean #t])
        (cond
          [(> top (+ (/ height 2) (* height vertical-endpoint-ref)))
           (context-update-displays!)]
          [(or (<= kernel-width minumum-shape-width)
               (<= kernel-height minumum-shape-height))
           ;If the procedure has recursed to the right size, stop and make some shapes.
           (cond
             ;If the boolean below holds, take a look at pattern-vector-1 to create the shape. After drawing,
             ;switch the shape procedure in pattern-vector-1 so we've got a new shape procedure for the next drawing.
             [(= (mod n 2) 0)
              ((vector-ref pattern-vector-1 0) image REPLACE
                                             left top minumum-shape-width minumum-shape-height)
              (context-set-fgcolor! (vector-ref colors (mod (+ n marker) 7)))
              (image-fill-selection! image)
              (context-update-displays!)
              (image-select-nothing! image)
              (if (equal? (vector-ref pattern-vector-1 0) image-select-rectangle!)
                  (vector-set! pattern-vector-1 0 image-select-ellipse!)
                  (vector-set! pattern-vector-1 0 image-select-rectangle!))]
             ;If the boolean below holds, take a look at pattern-vector-2 to create the shape. For each shape,
             ;iterate through pattern-vector-2 until you reach the counter at the end of the vector. Then
             ;reset the counter so you can keep drawing through the pattern of drawing procedures found in the
             ;vector.
             [(= (mod n 3) 0)
              ((vector-ref pattern-vector-2 (vector-ref pattern-vector-2 4)) 
               image REPLACE left top minumum-shape-width minumum-shape-height)
              (context-set-fgcolor! (vector-ref colors (mod (+ n marker) 7)))
              (image-fill-selection! image)
              (context-update-displays!)
              (image-select-nothing! image)
              (if (equal? (vector-ref pattern-vector-2 4) 3)
                  (vector-set! pattern-vector-2 4 0)
                  (vector-set! pattern-vector-2 4 (+ 1 (vector-ref pattern-vector-2 4))))]
             ;Otherwise, use typical recursion to draw your shape. The shape pattern is
             ;not bound to a mutable data structure, so it always creates the same pattern.
             [shape-boolean
              (image-select-rectangle! image REPLACE
                                       left top minumum-shape-width minumum-shape-height)
              (context-set-fgcolor! (vector-ref colors (mod (+ n marker) 7)))
              (image-fill-selection! image)
              (context-update-displays!)
              (image-select-nothing! image)]
             [else
              (image-select-ellipse! image REPLACE
                                     left top minumum-shape-width minumum-shape-height)
              (context-set-fgcolor! (vector-ref colors (mod (+ n marker) 7)))
              (image-fill-selection! image)
              (context-update-displays!)
              (image-select-nothing! image)])]
          [else
           ;Divide the space into four squares until minimum sizes are reached. Iterate markers
           ;to change drawing colors.
           (kernel left top (/ kernel-width 2) 
                   (/ kernel-height 2) (+ marker 3) (not shape-boolean))
           (kernel (+ left (/ kernel-width 2)) top (/ kernel-width 2)
                   (/ kernel-height 2) (+ marker 7) shape-boolean)
           (kernel left (+ top (/ kernel-height 2)) (/ kernel-width 2)
                   (/ kernel-height 2) (+ marker 13) shape-boolean)
           (kernel (+ left (/ kernel-width 2)) (+ top (/ kernel-height 2)) (/ kernel-width 2) 
                   (/ kernel-height 2) (+ marker 17) (not shape-boolean))])))))

;;; Procedures:
;;;   layer-2!
;;; Parameters:
;;;   image, an image
;;;   n, a natural number
;;; Purpose:
;;;   Render the second layer in image-series, consisting of
;;;   branching turtle graphics. Also, call layer-3! to
;;;   render the third layer.
;;; Produces:
;;;   [Nothing; Called for the side effect.]
;;; Preconditions:
;;;   image is a valid image
;;; Postconditions:
;;;   The second layer of imager-series has been rendered.
;;;   The turtles' movements will be drawn from a brush in
;;;     the brushes vector.
;;;   The color of the brush used will be one found in the
;;;     colors vector.
;;;   If the brush is scalable, it will be scaled according to
;;;     (/ (sqrt (* (image-width image) (image-height image))) 50)
;;;   The length of the branches in the pattern will vary according
;;;     to a number in the trunk-multipliers vector.
;;;   The initial trunk size is (/ (image-height image) 8).
;;; Acknowledgements:
;;;   This documentation and layer-2! partially based off
;;;   of the procedures found in the Project Ideas readings
;;;   from Professor Weinman at:
;;;   http://www.cs.grinnell.edu/~weinman/courses/CSC151/2014F/readings/project-color-trees-reading.html
;;; Notes:
;;;   Some inputs n will be very computationally heavy. Larger images exacerbate this issue.
(define layer-2!
  (lambda (image n)
    (let* ([turtle1 (turtle-new image)]
           [turtle2 (turtle-new image)]
           [width (image-width image)]
           [height (image-height image)]
           [turtle-list (list turtle1 turtle2)]
           ;Starting with two turtles is easier to code,
           ;although it does cause of overlapping of lines.
           [brushes (vector "1. Pixel"
                            "2. Block 01"
                            "2. Block 02"
                            "2. Block 03"
                            "2. Hardness 025"
                            "2. Hardness 050"
                            "2. Hardness 075"
                            "2. Hardness 100"
                            "Bristles 01"
                            "Bristles 02"
                            "Bristles 03")]
           [brush (vector-ref brushes (mod n 11))]
           [colors (vector (irgb 131 69 69) (irgb 131 84 69) ;19 brush colors
                           (irgb 131 98 69) (irgb 131 113 69)
                           (irgb 131 127 69) (irgb 120 131 69)
                           (irgb 113 69 131) (irgb 124 69 131)
                           (irgb 131 69 126) (irgb 131 69 100)
                           (irgb 131 69 85) (irgb 105 55 55)
                           (irgb 105 67 55) (irgb 105 79 55)
                           (irgb 105 90 55) (irgb 90 55 105)
                           (irgb 102 55 105) (irgb 105 55 96)
                           (irgb 105 55 70))]
           [trunk-multipliers (vector 3/2 4/3 5/4 6/5
                                      7/6 8/7 9/8 10/9
                                      11/10 12/11 13/12 14/13
                                      15/14)]
           [trunk-multiplier (vector-ref trunk-multipliers (mod n 13))]
           [brush-color (vector-ref colors (mod n 19))]
           ;Since n gets modified in this procedure, we set it aside so
           ;that the layer-3! procedure can make use of it.
           [layer-3!-seed n])
      (if (>= (mod n 11) 8)
          (map (r-s turtle-set-brush! brush) turtle-list) 
          ;If the brush is not scalable, don't try to scale it. That is, brushes 9-11 in the vector
          ;are not scalable.
          (map (lambda (turtle) (turtle-set-brush! turtle brush (/ (sqrt (* width height)) 50))) turtle-list))
      (map (r-s turtle-set-color! brush-color) turtle-list)
      (map (lambda (turtle) (turtle-teleport! turtle (/ width 2) height)) 
           turtle-list)
      (map (r-s turtle-turn! -90) 
           turtle-list)
      (map (lambda (turtle) (turtle-forward! turtle (/ height 4))) 
           turtle-list)
      (let kernel  ([n (+ (mod n 7) 1)] ; Restricts the number of possible branches.
                    [m (+ (mod n 2) 1)] ; Adds a second depth of recursive branching.
                    [turtle1 turtle1]
                    [turtle2 turtle2]
                    [turn-amount (* 5 (+ (mod n 23)) 1)]
                    [trunk (/ (image-height image) 8)])
        (cond
          [(= m 0)
           (layer-3! image turtle1 layer-3!-seed)]
          [(= n 0)
           (kernel (+ n 2) (- m 1) turtle1 (turtle-clone turtle1) 30 (/ height 8))
           (kernel (+ n 2) (- m 1) turtle2 (turtle-clone turtle2) 30 (/ height 8))]
          [else
           (turtle-turn! turtle1 turn-amount)
           (turtle-turn! turtle2 (* -1 turn-amount))
           (map (r-s turtle-forward! trunk) (list turtle1 turtle2))
           ;turtle-list from before cannot be used here.
           ;Below: Take turtle1 and turtle2, clone them, and set all turtles off in a different direction.
           (kernel (- n 1) m turtle1 (turtle-clone turtle1) (+ (* turn-amount 3/4) 1) (/ trunk trunk-multiplier))
           (kernel (- n 1) m turtle2 (turtle-clone turtle2) (+ (* turn-amount 3/4) 1) (/ trunk trunk-multiplier))])))))

;;; Procedures:
;;;   layer-3!
;;; Parameters:
;;;   image, an image
;;;   turtle, a turtle
;;;   n, a natural number
;;; Purpose:
;;;   Render one piece of the third layer in image-series, 
;;;   consisting of turtle graphics of various patterns.
;;; Produces:
;;;   [Nothing; Called for the side effect.]
;;; Preconditions:
;;;   image is a valid image
;;; Postconditions:
;;;   If called from within layer-2!, the third layer of image-series
;;;     has been rendered.
;;;   If (= (mod n 3) 0), then the turtle will turn 30 degrees,
;;;     and draw a line of length move-amt.
;;;   If (= (mod n 3) 1), then the turtle repeat move-and-turn!
;;;     up to five times with the modified parameters repeat-move-amt
;;;     and repeat-turn-amt.
;;;   If (= (mod n 3) 2), then the turtle will face -135 degrees from
;;;     the right edge of the image and draw a line of length move-amt.
;;;   The color of the brush to draw the turtle's (turtles') movements
;;;     will be one found in the colors vector.
;;;   The brush used by the turtle (turtles) will be the one it was
;;;     holding when layer-3! was called.
;;; Acknowledgements:
;;;   This documentation and layer-3! partially based off
;;;   of the procedures found in the Project Ideas readings
;;;   from Professor Weinman at:
;;;   http://www.cs.grinnell.edu/~weinman/courses/CSC151/2014F/readings/project-color-trees-reading.html
(define (layer-3! image turtle n)
  (let* ([colors (vector (irgb 241 59 102) (irgb 241 59 80)
                         (irgb 241 59 59)  (irgb 241 80 102)
                         (irgb 241 102 59) (irgb 241 123 59)
                         (irgb 241 145 59) (irgb 241 166 59)
                         (irgb 241 187 59) (irgb 241 209 59)
                         (irgb 255 253 149) (irgb 255 149 149)
                         (irgb 255 176 149))]
         [move-amt (sqrt (+ (image-width image)
                            (image-height image)))])
    (turtle-set-color! turtle (vector-ref colors (mod n 13)))
    (cond
      [(= (mod n 3) 0)
       (turtle-down! turtle)
       (turtle-turn! turtle 30)
       (turtle-forward! turtle move-amt)]
      [(= (mod n 3) 1)
       (let ([repeat-move-amt (* (/ 3/4 (mod n 11)) move-amt)]
             [repeat-turn-amt (/ 360 (mod n 17))])
         (repeat (mod n 5)
                 (lambda () (move-and-turn! turtle repeat-move-amt repeat-turn-amt))))]
      [else
       (turtle-down! turtle)
       (turtle-face! turtle -135)
       (turtle-forward! turtle move-amt)])))

;;; Procedures:
;;;   move-and-turn!
;;; Parameters:
;;;   turtle, a turtle
;;;   move-distance, a real number
;;;   turn-amount, a real number
;;; Purpose:
;;;   To move turtle by move-distance and then
;;;     turn turtle by turn-amount.
;;; Produces:
;;;   [Nothing; Called for the side effect.]
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   The turtle first moved by move-distance and turned
;;;     by turn-amount.
;;;   If the turtle's brush was down, the turtle has drawn
;;;     a line of length move-distance in the direction
;;;     it was facing with the procedure was called.
(define move-and-turn!
  (lambda (turtle move-distance turn-amount)
    (turtle-forward! turtle move-distance)
    (turtle-turn! turtle turn-amount)))