#lang racket
(require gigls/unsafe)
;;; Procedure:
;;;   image-series
;;; Parameters:
;;;   n, a positive integer
;;;   width, a positive integer
;;;   height, a positive integer
;;; Purpose:
;;;   create an interesting and unique image using the specified parameters
;;; Produces:
;;;;  an image, img
;;; Preconditions:
;;;   0 < n <= 1000
;;;   width and height are within a reasonable range
;;; Postconditions:
;;;   img scales reasonably
;;;   for n1 and n2 such that (= n1 n2) is #f, the images created by (image-series n1 width height) and (image-series n2 width height) look different
(define image-series 
  (lambda (n width height)
    (letrec (; Chooses a corner out of 8 possible corners to draw the curves
             ; (four corners of the image, and the four middle points of the sides)
             [random-corner
              (lambda ()
                (let* ([num (mod n 5)]
                       [corner (list (mod n 3) (mod (+ n num) 3))])
                  (if (= (car corner) (cadr corner) 1)
                      corner
                      (list (/ (car corner) 2) (/ (cadr corner) 2)))))]
             [corner (random-corner)]
             [x (car corner)]
             [y (cadr corner)]
             ; Chooses a random color out of the six possible hues
             [rand-color
              (lambda (n2)
                (color->rgb (vector-ref 
                             (vector "red" "yellow" "green" "cyan" "blue" "magenta") 
                             (mod n2 6))))]
             [color (rand-color n)]
             ; To use in image-gradient and other procedures, computes a color of a higher value with a given n2
             [rgb-calc
              (lambda (color n2)
                (rgb-new (+ (rgb-red color) n2)
                         (+ (rgb-green color) n2)
                         (+ (rgb-blue color) n2)))]
             ; Creates an image colored in gradient, getting higher in value towards the corner 
             [image-gradient
              (lambda ()
                (image-compute 
                 (lambda (col row)
                   (let* ([change (+ (+ col row) (* (- 1 y) (- width row row)) (* (- 1 x) (- height col col)))])
                     (rgb-calc 
                      (rgb-calc color 150)
                      (/ change (+ width height) 1.6 (/ 1 255)))))
                 width height))]
             [canvas (image-show (image-gradient))]
             ; Sets brush and opacity to prepare drawing pentagons
             [set-brush
              (lambda ()
                (gimp-brushes-set-opacity 30)
                (context-set-brush! "Nova"))]
             ; Calculates the sine of an angle measured in degrees
             [sin2
              (lambda (angle)
                (sin (/ (* angle pi) 180)))]
             ; Makes a list consisting (pseudo)random positions and radii for a (pseudo)random number of pentagons
             ; Adjusts positions and radii for width and height
             ; Makes sure that pentagons do not appear in the corner where curves will be drawn
             [random-positions
              (lambda ()
                (let kernel ([total (+ 4 (mod n 6))]
                             [n2 (+ n 5)])
                  (if (zero? total)
                      null
                      (let* ([wbound (* width (abs (- x 1)))]
                             [hbound (* height (abs (- y 1)))]
                             [left (round (* (mod (* n2 71) 1000) 0.001 width))]
                             [top (round (* (mod (* n2 41) 1000) 0.001 height))]
                             [radius (+ (round (* (mod (* n2 83) 1000) 0.001 (/ (min height width) 6.111))) (round (/ (min width height) 30)))])
                        (if (or (and (> left (min wbound (/ width 2))) (< left (max wbound (/ width 2)))) (and (> top (min hbound (/ height 2))) (< top (max hbound (/ height 2)))))
                            (cons (list left top radius)
                                  (kernel (- total 1) (+ n2 1109)))
                            (kernel total (+ n2 1109)))))))]
             ; For a given set of center and radius, calculates and makes a list of the corners of the pentagon
             [pentagon-coords
              (lambda (x y radius)
                (let* ([srule (/ radius (sin2 90))]
                       [xadd (round (* srule (sin2 72)))]
                       [yadd (round (* srule (sin2 18)))]
                       [adj (round (* srule (sin2 54)))]
                       [opp (round (* srule (sin2 36)))])
                  (list
                   (position-new x (- y radius))
                   (position-new (+ x xadd) (- y yadd))
                   (position-new (+ x opp) (+ y adj))
                   (position-new (- x opp) (+ y adj))
                   (position-new (- x xadd) (- y yadd)))))]
             ; For a given list of pentagon corners, draws the pentagons
             ; Strokes the pentagon first using the "Nova" brush, and then fills the pentagons with white
             [draw-pentagon!
              (lambda (lst)
                (context-set-fgcolor! color)
                (gimp-brushes-set-paint-mode 0)
                (gimp-context-set-brush-size (/ (caddr lst) 3))
                (image-select-polygon! canvas REPLACE (pentagon-coords (car lst) (cadr lst) (caddr lst)))
                (image-stroke-selection! canvas)
                (context-set-fgcolor! "white")
                (image-fill-selection! canvas)
                (image-select-nothing! canvas))]
             ; Returns suitable turtle brush for image size
             [change-brush
              (lambda ()
                (cond [(< (min width height) 50) "2. Hardness 025"]
                      [(< (min width height) 100) "2. Hardness 050"]
                      [(< (min width height) 150) "2. Hardness 075"]
                      [else "2. Hardness 100"]))]
             ; Draws the curves using turtles, varying the number of curves, the length of each step, and the degree of angle increment
             ; Some code to draw spiral taken from previous lab
             [draw-turtle
              (lambda ()
                (letrec ([total (+ 12 (mod n 8))]
                         [angle (/ 360 total)]
                         [yertle (turtle-new canvas)]
                         [turtle-adjust
                          (lambda (turtle)
                            (turtle-teleport! turtle (* width x) (* height y))
                            (turtle-set-brush! turtle (change-brush))
                            (turtle-set-color! turtle (rgb-calc color -100))
                            turtle)]
                         [turtles (map turtle-clone (make-list total (turtle-adjust yertle)))]
                         [turtle-action!
                          (lambda (turtle angle2 distance)
                            (turtle-forward! turtle distance)
                            (turtle-turn! turtle angle2))]
                         [turtle-spiral!
                          (lambda (turtle n2)
                            (let*
                                ([steps (+ 20 (mod n2 8))]
                                 [distance (* (+ 3 (mod n2 4)) (min width height) .005)]
                                 [divisor (+ 2 (mod n2 11))])
                              (for-each (lambda (angle2) (turtle-action! turtle angle2 distance))
                                        (map (r-s / divisor) (list-drop (iota (+ 1 steps)) 1)))))])
                  (gimp-brushes-set-opacity 100)
                  (for-each turtle-turn! turtles (map (l-s * angle) (iota total)))
                  (for-each (lambda (turtle n2) (turtle-spiral! turtle n2)) turtles (map (l-s * 11) (iota total)))))])
      (set-brush)
      (map draw-pentagon! (random-positions))
      (draw-turtle)
      canvas)))