#lang racket
(require gigls/unsafe)

;(context-set-fgcolor! (color-name->irgb "blue"))
;Whole picture

;;; Procedure:
;;;   draw-picture
;;; Parameters:
;;;   n, a positive number [unverified]
;;;   width, a positive integer [unverified]
;;;   height, a positive integer [unverified]
;;; Purpose:
;;;   Produce an image of width width and height height that has 3 components: The background, a color gradient;
;;;   a set of 7 polygons in the lower right arranged along a hyperbola; the dragon curve, a fractal pattern that vaguely resembles a dragon.
;;; Produces:
;;;   image, an image
;;; Preconditions:
;;;   0<=n<1000
;;;   height > 0
;;;   width > 0
;;; Postconditions:
;;;   (image-width image) = width
;;;   (image-height image) = height
;;;   All the various artistic requirements are met.

(define (draw-picture n width height)
  (let* ([param1 (truncate (/ n 100))] ;the hundreds place
         [param2 (mod (truncate (/ n 10)) 10)] ;the tens place
         [param3 (mod n 10)] ;ones place
         [image (compute-background width height param3)]
         [turtle (turtle-new image)])
    (turtle-up! turtle)
    (draw-hyperbola! turtle 
                     (+ 3 param2))
    (image-select-nothing! image)
    (turtle-down! turtle)
    ;If the image is oriented vertically, draw the dragon curve vertically.
    (cond [(> (image-height image) (image-width image))
           (turtle-turn! turtle 90)
           ;teleports the turtle to a reasonable starting place, draws curve
           (turtle-teleport! turtle 
                             (* .4 (image-width image)) 
                             (* .23 (image-height image))) 
           (dragon-curve! turtle 
                          (+ 2 param1) 
                          (* .6 (image-width image))
                          1 -1 param1)]
          [else
            ;teleports the turtle to a reasonable starting place, draws curve
           (turtle-teleport! turtle 
                             (* .2 (image-width image)) 
                             (* .23 (image-height image))) 
           (dragon-curve! turtle 
                          (+ 2 param1) 
                          (* .6 (image-width image))
                          1 -1 param1)])))

;Dragon curve

;;; Procedure:
;;;   dragon-curve!
;;; Parameters:
;;;   turtle, a turtle
;;;   n, an integer
;;;   turn1, an integer
;;;   turn2, an integer
;;; Purpose:
;;;   to draw a dragon curve, as described at http://en.wikipedia.org/wiki/Dragon_curve
;;; Produces:
;;;   nothing, called for side-affect
;;; Preconditions:
;;;   turtle is defined in a defined world
;;;   the world must be open before calling the procedure.
;;;   (< distance (* .8 width))
;;;   (or (= turn1 -1) (= turn1 1)) must hold
;;;   (= turn2 -turn1) must hold
;;; Postconditions:
;;;   n=0
;;;   turtle has moved distance to the right
;;;   Curve changes color appropriately; resembles the expected image.

(define dragon-curve!
  (lambda (turtle n distance turn1 turn2 color-val)
    (let ([width (image-width (turtle-world turtle))]
          [height (image-height (turtle-world turtle))]
          [col (turtle-col turtle)]
          [row (turtle-row turtle)])
      (cond
        [(= n 0)
         (turtle-set-color! turtle (irgb
                                    (if (>= color-val 4)
                                        (* 255 (/ (truncate (+ col (/ 255 (+ 1 color-val)))) width)) 
                                        0)
                                    (if (< color-val 7)
                                        (- 255 (* 255 (/ (truncate (+ row (/ 255 (+ 1 color-val)))) height))) 
                                        0)
                                    (cond
                                      [(< color-val 4)
                                       (* 255 (/ (truncate (+ col (/ 255 (+ 1 color-val)))) width))] ; added one to n to prevent a divide by 0 error
                                      [(>= color-val 7)
                                       (- 255 (* 255 (/ (truncate (+ row (/ 255 (+ 1 color-val)))) height)))]
                                      [else 
                                       0])))
;                                    (* 255 (/ (truncate (+ (turtle-row turtle) (/ 255 (+ 1 n)))) (image-height (turtle-world turtle))))
;                                    (* 255 (/ (truncate (+ (turtle-col turtle) (/ 255 (+ 1 n)))) (image-width (turtle-world turtle))))
;                                    (- 255 (* 255 (/ (truncate (+ (turtle-row turtle) (/ 255 (+ 1 n)))) (image-width (turtle-world turtle)))))))

         (turtle-forward! turtle distance)]
        [else
         (turtle-turn! turtle (* turn1 45))
         (dragon-curve! turtle (- n 1) (/ distance (sqrt 2)) 1 -1 color-val) ;recurs on itself with reduced distance and n
         (turtle-turn! turtle (* turn2 90))
         (dragon-curve! turtle (- n 1) (/ distance (sqrt 2)) -1 1 color-val)
         (turtle-turn! turtle (* turn1 45))]))))

;Background

;;; Procedure:
;;;   compute-background
;;; Parameters:
;;;   width, a number
;;;   height, a number
;;;   n, an integer
;;; Purpose:
;;;   to use image-compute to create a 2 color gradient in which the saturation of each of the colors varies inversely to the other 
;;; Produces:
;;;   image, an image
;;; Preconditions:
;;;   -1<n<10
;;;   height > 0
;;;   width > 0
;;; Postconditions:
;;;   image is colored
;;;   Colors change across a gradient.
;;;   If n < 4, red component increases from left to right, green component decreases from top to bottom, blue component = 0
;;;   If 4 <= n < 7, red component = 0, green component decreases from top to bottom, blue increases from left to right.
;;;   If n >= 7, red component decreases from top to bottom, green component = 0, blue component increases from left to right.

(define (compute-background width height n)
  (image-compute (lambda (col row)
                   (irgb (cond
                           [(< n 4)
                            (* 255 (/ (truncate (+ col (/ 255 (+ 1 n)))) width))] ; added one to n to prevent a divide by 0 error
                           [(>= n 7)
                            (- 255 (* 255 (/ (truncate (+ row (/ 255 (+ 1 n)))) height)))]
                           [else 
                            0])
                         (if (< n 7)
                             (- 255 (* 255 (/ (truncate (+ row (/ 255 (+ 1 n)))) height))) 
                             0)
                         (if (>= n 4)
                             (* 255 (/ (truncate (+ col (/ 255 (+ 1 n)))) width)) 
                             0))) width height)) ;this system of conditionals insures that for any value of n, any given pixel will have exactly 2 rgb components > 0


;;;This procedure was inspired by our work for Problem 1 on Assignment 7: Producing Playful Polygons

;;; Procedure:
;;;   turtle-polygon!
;;; Parameters:
;;;   turtle, a turtle
;;;   side-length, a number
;;;   sides, an integer
;;; Purpose:
;;;   Select a regular polygon with sides sides and side length side-length.
;;; Produces:
;;;   Nothing, called for side-affect
;;; Preconditions:
;;;  0 < side-length
;;;  2 < sides
;;;  turtle is defined in a defined world
;;;  the world must be open before calling the procedure.  
;;; Postconditions:
;;;  All sides of the selection have equal length, side-length
;;;  The interior angle of each vertex is (interior-angle (/ (* 180 (- sides 2)) sides))
;;;  A polygon with each of the previous postconditions is selected in GIMP

(define (turtle-polygon! turtle side-length sides)
  (let kernel ([turtle turtle]
               [side-length side-length]
               [sides sides]
               [vertices null]
               [n sides])
    (cond [(= n 0)
           (image-select-polygon! (turtle-world turtle) REPLACE vertices)]
          [else
           (turtle-forward! turtle side-length)
           (turtle-turn! turtle (- 180 (/ (* 180 (- sides 2)) sides)))
           (kernel turtle side-length sides (cons (turtle-point turtle) vertices) (- n 1))])))  ;creates a list of vertices using cons to add the turtle's position to a list at every vertex.

;;;This procedure is based on one presented in Assignment 7, but we added some key features.

;;;Procedure:
;;;  turtle-centered-polygon!
;;;Parameters:
;;;  turtle, a turtle
;;;  radius, a real number
;;;  sides, a positive integer
;;;Purpose:
;;;  Draw a colored regular polygon with sides sides centered at the point at which the turtle starts.
;;;  The sides are radius away from the starting point, and the side length is thus calculated from radius.
;;;Produces:
;;;  Nothing, called for side effect
;;;Preconditions:
;;;  radus, sides must be positive
;;;  turtle is defined in a defined world
;;;  the world must be open before calling the procedure.
;;;Postconditions:
;;;  All sides have equal length, where side-length = (* 2 radius (sin (/ pi sides)))
;;;  The interior angle of each vertex is (interior-angle (/ (* 180 (- sides 2)) sides))
;;;  All vertices are radius away from the turtle's starting point
;;;  A polygon with each of the previous postconditions is selected in GIMP

(define turtle-centered-polygon! 
  (lambda (turtle radius sides)
    (let* ([interior-angle (/ (* 180 (- sides 2)) sides)]
           ;the color of the background at the center of each polygon:
           [original-color (image-get-pixel (turtle-world turtle) 
                                            (turtle-col turtle) 
                                            (turtle-row turtle))] ;the color of the background at the center of each polygon 
           [w (image-width (turtle-world turtle))]
           [h (image-height (turtle-world turtle))])
      ;make the foreground lighter to give just the right amount of contrast with the background:
      (context-set-fgcolor! (irgb (+ (rgb-red original-color) 64) 
                                  (+ (rgb-green original-color) 64) 
                                  (+ (rgb-blue original-color) 64))) 
      (turtle-set-color! turtle (irgb 0 (turtle-row turtle) (turtle-col turtle)))
      (turtle-forward! turtle radius)
      (turtle-turn! turtle (- 180 (/ interior-angle 2)))
      (turtle-polygon! turtle (* 2 radius (sin (/ pi sides))) sides)
      (turtle-turn! turtle (/ interior-angle 2))
      (turtle-forward! turtle radius)
      (turtle-turn! turtle 180)
      (image-fill-selection! (turtle-world turtle))
      (context-set-brush! "1. Pixel" 5)))) 

;;;Placing polygons along a hyperbola    
;;; Procedure:
;;;   draw-hyperbola!
;;; Parameters:
;;;   turtle, a turtle
;;;   sides, an integer
;;; Purpose:
;;;   draw 7 polygons of sides sides placed along a hyperbola
;;; Produces:
;;;   image, an image
;;; Preconditions:
;;;   turtle is defined in a defined world
;;;   the world must be open before calling the procedure
;;; Postconditions:
;;;   image has 7 polygons of sides sides arrayed across a hyperbola
;;;   The center polygon is the largest, with smaller ones emanating outwards on each side.
;;;   turtle has moved to the center of the top polygon

(define (draw-hyperbola! turtle sides)
  (let* ([image (turtle-world turtle)]
         [x-values (list 5.64 2.512 1.52 1 .657 .397 .177)] 
         [y-values (list .177 .397 .657 1 1.52 2.512 5.64)] ;these list contain carefully selected x and y values on the hyperbola
         [radius-scales (list 11.39 5.06 2.25 1 2.25 5.06 11.39)]
         [width (image-width image)]
         [height (image-height image)]
         [axis-scale (if (> height width)
                         (/ width 90)
                         (/ height 90))])
    (let kernel ([x-values x-values]
                 [y-values y-values]
                 ;normalize radius scales to the size of the image:
                 [radius-scales (map (r-s * (* 3 axis-scale)) radius-scales)])
      (cond [(null? x-values)
             (image-show image)]
            [else
             ;this let* normalizes the positions of the polygons relative to the lengths of the axes
             (let* ([x (- width (* (/ (car x-values) 10) width))] 
                    [y (- height (* (/ (car y-values) 10) height))]
                     ;sets the size of the polgons based on the ratio of the real x and y and the radius scale, making it proportional to the image size.
                    [radius (if (> (car x-values) (car y-values))
                                (* (car radius-scales) (/ (car y-values) (car x-values))) 
                                (* (car radius-scales) (/ (car x-values) (car y-values))))])
               (turtle-teleport! turtle x y)
               (turtle-centered-polygon! turtle radius sides) ;draws polygon
               (kernel (cdr x-values) ;recurs down the lists
                       (cdr y-values)
                       (cdr radius-scales)))]))))



;;;Suggested calls:

;;; n = 624
;;; n = 739
;;; n = 991

;;;Aspect ratios relatively close to 1:1 look best, though all work.