#lang racket
(require gigls/unsafe)

; A vector containing all the corners, or points of our shape
(define corners
  (make-vector 8 null))

; A unit vector counting the index of the corner that will be recorded
(define counter
  (make-vector 1 0))

;Above two vectors and their use in the program are inspired by
; work of Prof. Samuel Rebelsky on a helping e-mail to his class

;;; Procedure
;;;  turtle-forward-turn!
;;; Parameters
;;;  turtle, a gigls turtle
;;;  angle, a number
;;;  side-length, a positive number
;;; Purpose
;;;  Make a turtle go forward and then turn by some degree
;;; Produces
;;;  None (side-effect procedure)
;;; Preconditions
;;;  None additional
;;; Postconditions
;;;  turtle moves forward by side-length
;;;  turtle then turns by 'angle' degrees

(define turtle-forward-turn!
  (lambda (turtle angle side-length)
    (turtle-forward! turtle side-length)
    (vector-set! corners (vector-ref counter 0) (turtle-point turtle))
    (vector-set! counter 0 (+ 1 (vector-ref counter 0)))
    (turtle-turn! turtle angle)))

;;; Procedure
;;;  draw-polygon!
;;; Parameters
;;;  image, a gigls image
;;;  sides-n, an integer
;;;  tommy, a gigls turtle
;;; Purpose
;;;  Make a centered polygon on an image
;;; Produces
;;;  None (side-effect)
;;; Preconditions
;;;  sides-n is one of three values: 0, 1, 2.
;;; Postconditions
;;;  If sides-n is 0, then
;;;   a centered pentagon of side length equal to one third of the image width
;;;    is painted on image
;;;   The angles at the points of the pentagon are equal to 108
;;;  If sides-n is 1, then
;;;   a centered hexagon of side length equal to one third of the image width
;;;    is painted on image
;;;   The angles at the points of the hexagon are equal to 120
;;;  If sides-n is 2, then
;;;   a centered octagon of side length equal to one third of the image width
;;;    is painted on image
;;;   The angles at the points of the octagon are equal to 135
;;;  The polygon is colored with the foreground color in the GIMP context, meaning
;;;   (context-get-fgcolor!)

(define draw-polygon!
  (lambda (image sides-n tommy)
    (let* ([sides (inexact->exact (+ 5 (expt sides-n 1.5849625007211563)))]
           ;Above numeric calculation converts 0, 1, 2, into 5, 6, 8, respectively
           [angle (/ 360 sides)]
           [side-length (/ (image-width image) 3)] ;Proportions
           [x side-length] ;Proportions
           [y (/ (- (image-height image)
                    (* side-length (list-ref (list 1.3763819173540548
                                                   1.7320508117569697
                                                   2.414213562373095) sides-n))) 2)])
                                             ;values in above list drawn from
                                             ; trigonometric calculations that
                                             ; give us the correct proportions
      (turtle-teleport! tommy x y)
      (repeat sides turtle-forward-turn! tommy angle side-length))))

; turtle-forward-turn! in combination with draw-polygon!
;  definitions heavily based on turtle-polygon! procedure in project-ideas-lab.rkt
;  URL: http://www.cs.grinnell.edu/~weinman/courses/CSC151/2014F/code/project-ideas-lab.rkt
;  Work by Samuel Rebelsy, Jerod Weinman, et. all.

;;; Procedure
;;;  draw-pentagram!
;;; Parameters
;;;  image, a gigls image
;;;  filled, a boolean
;;;  tommy, a gigls turtle
;;; Purpose
;;;  Make a centered pentagram on an image
;;; Produces
;;;  None (side-effect)
;;; Preconditions
;;;  tommy is a turtle in image, that is
;;;   (turtle-world tommy) = image.
;;; Postconditions
;;;  A pentagram with side length equal to 2/3 of the image width is drawn on image
;;;  If filled is #t, then the pentagram's "blades" are filled with color, while its "core",
;;;   meaning the pentagram on its center, is not filled.
;;;  If filled is #f, then the pentagram is only outlined.
;;;  The pentagram is colored with the foreground color in the GIMP context, meaning
;;;   (context-get-fgcolor!)

(define draw-pentagram!
  (lambda (image filled tommy)
    (let* ([x (/ (image-width image) 6)]
           [side-length (* 4 x)]
           [y (/ (- (image-height image)
                    (* 0.19098300636944165 side-length)) 2)])
      (turtle-teleport! tommy x y)
      (repeat 5 turtle-forward-turn! tommy 144 side-length)
      (when filled
        (image-select-polygon! image REPLACE (list-take (vector->list corners)
                                                        5))))))

;;; Procedure
;;;  draw-hexagram!
;;; Parameters
;;;  image, a gigls image
;;;  filled, a boolean
;;;  tommy, a gigls turtle
;;; Purpose
;;;  Make a centered hexagram on an image
;;; Produces
;;;  None (side-effect)
;;; Preconditions
;;;  tommy is a turtle in image, that is
;;;   (turtle-world tommy) = image.
;;; Postconditions
;;;  A hexagram with side length equal to 3/5 of the image width is drawn on image
;;;  If filled is #t, then the hexagram's "blades" are filled with color, while its "core",
;;;   meaning the hexagram on its center, is not filled.
;;;  If filled is #f, then the hexagram is only outlined.
;;;  The hexagram is colored with the foreground color in the GIMP context, meaning
;;;   (context-get-fgcolor!)

(define draw-hexagram!
  (lambda (image filled tommy)
    (let* ([x (/ (image-width image) 5)] ;calculated using trigonometry
           [side-length (* x 3)]
           [y1 (/ (image-height image) 3)]
           [y2 (* y1 2)])
      (turtle-teleport! tommy x y1)
      (repeat 3 turtle-forward-turn! tommy 120 side-length)
      ;A hexagram is composed of two triangles. Thus, we will draw another one.
      (turtle-teleport! tommy x y2)
      (turtle-face! tommy 300)
      (repeat 3 turtle-forward-turn! tommy 120 side-length)
      (when filled ;if we want it to be filled, select region to be filled
        (image-select-polygon! image REPLACE (list-take (vector->list corners)
                                                        3)) ;two triangles
        (image-select-polygon! image ADD (list-drop (list-take (vector->list corners)
                                                        6) 3))))))

;;; Procedure
;;;  draw-octagram!
;;; Parameters
;;;  image, a gigls image
;;;  filled, a boolean
;;;  tommy, a gigls turtle
;;; Purpose
;;;  Make a centered octagram on an image
;;; Produces
;;;  None (side-effect)
;;; Preconditions
;;;  tommy is a turtle in image, that is
;;;   (turtle-world tommy) = image.
;;; Postconditions
;;;  A octagram with side length equal to 3/5 of the image width is drawn on image
;;;  If filled is #t, then the octagram's "blades" are filled with color, while its "core",
;;;   meaning the octagram on its center, is not filled.
;;;  If filled is #f, then the octagram is only outlined.
;;;  The octagram is colored with the foreground color in the GIMP context, meaning
;;;   (context-get-fgcolor!)

(define draw-octagram!
  (lambda (image filled tommy)
    (let* ([x1 (/ (image-width image) 5)]
           [x2 (/ (image-width image) 2)]
           [side-length (* x1 3)]
           [y1 x1]
           [y2 (* (image-width image) (/ 7 100))])
      (turtle-teleport! tommy x1 y1)
      (repeat 4 turtle-forward-turn! tommy 90 side-length)
      ;Similar to the hexagon, an octagon is composed of two squares.
      (turtle-teleport! tommy x2 y2)
      (turtle-face! tommy 45)
      (repeat 4 turtle-forward-turn! tommy 90 side-length)
      (when filled
        (image-select-polygon! image REPLACE (list-take (vector->list corners)
                                                        4))
        (image-select-polygon! image ADD (list-drop (vector->list corners)
                                                        4))))))

; draw-pentagram!, draw-hexagram! and draw-octagram! definitions heavily
;  based on draw-polygon! procedure above.

;;; Procedure
;;;  draw-polygram!
;;; Parameters
;;;  image, a gigls image
;;;  sides, a positive integer
;;;  filled, a boolean
;;;  tommy, a gigls turtle
;;; Purpose
;;;  Make a centered polygram (pentagram, hexagram or octagram) on an image
;;; Produces
;;;  None (side-effect)
;;; Preconditions
;;;  tommy is a turtle in image, that is
;;;   (turtle-world tommy) = image.
;;;  sides is one of the following values: 0, 1, 2
;;; Postconditions
;;;  If sides = 0, then
;;;   a pentagram will be drawn using the draw-pentagram! procedure
;;;  If sides = 1, then
;;;   a pentagram will be drawn using the draw-pentagram! procedure
;;;  If sides = 2, then
;;;   a pentagram will be drawn using the draw-pentagram! procedure

(define draw-polygram!
  (lambda (image sides fill tommy)
    (cond [(zero? sides)
           (draw-pentagram! image fill tommy)]
          [(= 1 sides)
           (draw-hexagram! image fill tommy)]
          [else
           (draw-octagram! image fill tommy)])
    (when fill
      (image-fill-selection! image)))) ;if fill is true, above procedures will
                                       ;have selected the region, so we can fill it

;;; Procedure
;;;  fill-polygon!
;;; Parameters
;;;  image, a gigls image
;;;  sides, a positive integer
;;;  tommy, a gigls turtle
;;; Purpose
;;;  Fill a selected polygon on an image
;;; Produces
;;;  None (side-effect)
;;; Preconditions
;;;  tommy is a turtle in image, that is
;;;   (turtle-world tommy) = image.
;;;  sides is one of the following values: 0, 1, 2
;;; Postconditions
;;;  If sides = 5, then
;;;   a pentagon will be drawn using the draw-polygon! procedure
;;;  If sides = 6, then
;;;   a hexagon will be drawn using the draw-polygon! procedure
;;;  If sides = 8, then
;;;   a octagon will be drawn using the draw-polygon! procedure
;;;  After the polygon is drawn (outlined), its interior is filled
;;;   with color.
;;;  The polygon is colored with the foreground color in the GIMP context, meaning
;;;   (context-get-fgcolor!)

(define fill-polygon!
  (lambda (image sides tommy)
    (draw-polygon! image sides tommy)
    (image-select-polygon! image REPLACE
                           (list-take (vector->list corners)
                                      (inexact->exact (+ 5 (expt sides 1.5849625007211563)))))
    (image-fill-selection! image)))

;;; Procedure
;;;  image-series
;;; Parameters
;;;  n, an integer
;;;  width, a positive number
;;;  height, a positive number
;;; Purpose
;;;  Create an interesting, unique image
;;; Produces
;;;  interesting-image, an image
;;; Preconditions
;;;  n is within the range 0-1154, inclusive that is:
;;;   0 <= n <= 1154
;;;  width <= height, preferrably width = height
;;;   (this is because the procedure mainly works with a
;;;    1:1 aspect ratio, because it uses turtles. Additionally, because it
;;;    uses width to calculate the side length of the shapes, it works partially
;;;    on aspect ratios lesser than 1:1, though artifacts and non-centered objects
;;;    can result.)
;;; Postconditions
;;;  (image-width interesting-image) = width
;;;  (image-height interesting-image) = height
;;;  Depending on the n input, the image may have different colors, shapes
;;;   and properties. The background color will be selected from a list of
;;;   the following colors:
;;;    hotpink, burlywood, goldenrod, purple, green, gold, wheat, lightblue, grey, brown, mintcream
;;;  The foreground color (color of the shape) will be selected from a list of
;;;   the following colors:
;;;    lavender, aqua, silver, cornflowerblue, red, yellow, orange
;;;  Depending on the n input, the shape might be a canonical polygon of sides
;;;   5, 6, or 8, or a polygram, a star-like shape with 5, 6, or 8 points.
;;;  Depending on the n input, the shape might be partially or fully filled with
;;;   color
;;;  Depending on the n input the shape might also be a filled ellipse

(define image-series
  (lambda (n width height)
    (let* ([sides-n (modulo n 3)]
           [shape-type-n (modulo n 5)]
           [fg-color-n (modulo n 7)]
           [bg-color-n (modulo n 11)]
           [fg-colors (list "lavender" "aqua" "silver" "cornflowerblue" "red" "yellow" "orange")]
           [bg-colors (list "hotpink" "burlywood" "goldenrod" "purple" "green" "gold" "wheat" "lightblue" "grey" "brown" "mintcream")])
      (context-set-bgcolor! (list-ref bg-colors bg-color-n))
      (context-set-fgcolor! (list-ref fg-colors fg-color-n))
      (let* ([canvas (image-new width height)]
            [tommy (turtle-new canvas)])
           ;Above numeric procedure converts sides-n, which takes values
           ; of 0, 1, 2, to values of 5, 6, 8, since those are the sides
           ; we are interested in.
        (turtle-down! tommy)
        (turtle-set-color! tommy (context-get-fgcolor))
        (cond
          [(= shape-type-n 0)
           (image-select-ellipse! canvas REPLACE (* 1/4 (image-width canvas)) (* 1/4 (image-height canvas)) (* 1/2 (image-width canvas)) (* 1/2 (image-height canvas)))
           (image-fill-selection! canvas)]
          [(= shape-type-n 1)
           (draw-polygon! canvas sides-n tommy)]
          [(= shape-type-n 2)
           (draw-polygram! canvas sides-n #f tommy)]
          [(= shape-type-n 3)
           (fill-polygon! canvas sides-n tommy)]
          [else
           (draw-polygram! canvas sides-n #t tommy)])
        (image-select-nothing! canvas);show the image
        (vector-set! counter 0 0)
        (image-show canvas))))) ;reset the counter, so that other images
                                      ;can still be drawn

; Three interesting n values for image-series are: 8, 60, 999 

;Special thanks to Computer Science student Ryan Galang for showing us the work
; of Professor Samuel Rebelsky on counter and corner

;General knowledge drawn from CSC-151-02 website, readings and labs:
; http://www.cs.grinnell.edu/~weinman/courses/CSC151/2014F/