#lang racket
(require gigls/unsafe)

;;; Procedure:
;;;   image-series
;;; Parameters:
;;;   n, an integer
;;;   width, an integer
;;;   height, an integer
;;; Purpose:
;;;   Creates an image which depends on n with the specified width and hieght
;;; Produces:
;;;   fractal-plant, an image
;;; Preconditions:
;;;   n is an integer between 0 and 999 inclusive
;;;   width is a positive integer
;;;   height is a positive integer
;;; Postconditions:
;;;   fractal-plant is an image
;;;   Each value of n produces a distinct fractal-plant.
;;;   fractal-plant satisfies (image-width fractal-plant)=width
;;;   fractal-plant satisfies (image-height fractal-plant)=height
;;;   fractal-plant is composed of a vertical gradient 'background', 
;;;     and a 'fractal' resembling a plant in the foreground
;;;   'background' is a smooth blend between two colors, which are determined by
;;;     n modulo 7.
;;;   'fractal' begins at the (0, height) coordinate, and grows at an angle 
;;;     determined by width and height. 
;;;   'fractal' is a fractal which is iterated (n modulo 5) + 2 times.
;;;   The color of the ends of 'fractal' is determined by n modulo 11.
;;;   The size of 'fractal' is determined by width, height, and n modulo 3.  
;;; Props: 
;;;   The following website provided our inspiration for fractal plant:
;;;     http://en.wikipedia.org/wiki/L-system
;;;   Ajuna Kyaruzi helped us figure out how to return an image without showing it. 
;;;   In writing the color-blend local helper procedure, we referred to code using 
;;;     image-compute-pixels! from the lab on building images by iterating over 
;;;     positions to get ideas for how to use image-recompute! to implement a 
;;;     procedure to produce a vertical gradient for our background. The code from 
;;;     the lab was written by Braden Brown, Gregory Garcia, and China Mauck.


(define image-series
  (lambda (n width height)
    (letrec 
         ; Generate a two-color blend that will be used as the background for fractal-plant.
        ([color-blend
          (λ (color1 color2 width height)
            (let ([canvas (image-new width height)]
                  [color1 (color-name->irgb color1)]
                  [color2 (color-name->irgb color2)])
              (image-select-all! canvas)
              (image-recompute! canvas
                                (λ ( col row)
                                  ; Take the difference between each component of color1 and color2 and divide
                                  ; by height to determine the increment per row.
                                  (irgb    (- (rgb-red color1) 
                                              (* (/ (- (rgb-red color1) (rgb-red color2)) height) row)) 
                                           (- (rgb-green color1) 
                                              (* (/ (- (rgb-green color1) (rgb-green color2)) height) row))
                                           (- (rgb-blue color1) 
                                              (* (/ (- (rgb-blue color1) (rgb-blue color2)) height) row)))))))] 
         ; Implement color-blend to generate the seven possible backgrounds for fractal-plant.
         ; The pair color1 and color2 that is used is determined by n modulo 7.
         [background (cond
                       [(= 0 (modulo n 7)) (color-blend "steelblue" "navy" width height)]
                       [(= 1 (modulo n 7)) (color-blend "gold" "lawngreen" width height)]
                       [(= 2 (modulo n 7)) (color-blend "skyblue" "seagreen" width height)]
                       [(= 3 (modulo n 7)) (color-blend "dodgerblue" "forestgreen" width height)]
                       [(= 4 (modulo n 7)) (color-blend "orangered" "orange" width height)]
                       [(= 5 (modulo n 7)) (color-blend "purple" "seagreen" width height)]
                       [(= 6 (modulo n 7)) (color-blend "black" "navy" width height)])]
         ; Add a turtle to the bottom left corner of background.
         [Atuin (turtle-teleport! (turtle-new background) 0 height)]
         ; Define the distance the turtle will travel in the first step of fractal-plant by
         ; using predetermined ratios that scale appropriately with the width and height of the image.
         ; The three possible distances depend on n modulo 3.
         [distance (cond 
                     [(= 0 (modulo n 3)) (* (sqrt (+ (expt ( * height 0.27189233611099495) 2) 
                                                     (expt ( * width 0.12678547852220984) 2))) (/ 2 3))]
                     [(= 1 (modulo n 3))    (sqrt (+ (expt ( * height 0.27189233611099495) 2) 
                                                     (expt ( * width 0.12678547852220984) 2)))]
                     [(= 2 (modulo n 3)) (* (sqrt (+ (expt ( * height 0.27189233611099495) 2) 
                                                     (expt ( * width 0.12678547852220984) 2))) (/ 4 3))])]
         ; Define the initial angle the turtle turns to be an appropriate angle for the size of the image. 
         ; angle is dependent on the width and height of the image.
         [angle (* (/ 180 pi) (atan (/ ( * height 0.27189233611099495) 
                                       ( * width 0.12678547852220984))))]
         ; Choose the color for the ends of fractal-plant based on n modulo 11.
         [color (list-ref  
                 (list "pink" "thistle" "lemonchiffon" 
                       "cornflowerblue" "purple" "crimson" 
                       "yellow" "coral" "maroon" "violet" "steelblue") 
                 (modulo n 11))]
         ; Define the fractal, using previous definitions and procedures.
         [fractal-plant
          (λ (turtle distance k)
            ; Set the brush to an appropriate size: 1 when k=0, or 2 when k>0.
            (turtle-set-brush!  turtle "2. Hardness 100" (min 2 (+ k 1)))
            ; Set the color of the different iterations of fractal plant.
            (cond
              [(= k 0) (turtle-set-color! turtle color)]
              [(= k 1) (turtle-set-color! turtle "yellowgreen")]
              [else (turtle-set-color! turtle "olive")])
            ; Turn the turtle counterclockwise the appropriate angle to begin the fractal iteration.
            (turtle-turn! turtle (- 0 angle))
            ; Move the turtle forward to begin drawing the fractal.
            (turtle-forward! turtle distance)
            ; If k>0, create three clones of turtle, which move and turn in distinct ways to create
            ; the fractal. For each of the four turtles, recursively call fractal-plant with k-1 iterations and distance/2.
            ; If k=0, move the turtle forward half the given distance.
            (cond 
              [(= 0 k) (turtle-forward! turtle (/ distance 2))]
              [else (for-each fractal-plant (list (turtle-turn! (turtle-clone turtle) angle)
                                                  (turtle-turn! (turtle-forward! (turtle-clone turtle) 
                                                                                 (/ distance 3)) (+ angle 15))
                                                  (turtle-turn! (turtle-forward! (turtle-clone turtle) 
                                                                                 (- 0 (/ distance 5))) (+ angle 20))
                                                  (turtle-turn! turtle (- angle 25))) 
                              (make-list 4 (/ distance 2))
                              (make-list 4 (- k 1)))]))])
      ; Implement fractal-plant with iterations between 2 and 6, inclusive, dependent
      ; on n modulo 5.
      (fractal-plant Atuin distance (+ (modulo n 5) 2))
      background)))



