;;;; Problem Solving in Computer Science (Prof. Dave Hollinger)
;;;; Homework #2a (due Sept. 17, 2004)
;;;;
;;;; Submitted by: Benjamin Levinn

;;; Global notes: All of these require the draw.ss teachpack, and drawing functions require
;;; an open canvas.

;; FUNCTION base-angle
;;
;; SYNTAX:
;;   (base-angle pos1 pos2)
;;
;; DESCRIPTION
;;   Given any two points, finds the reference angle between the line segment connecting
;;   them and the x-axis (determined by the first point)
;;
;; INPUT:
;;   (posn) pos1 : The first point (the origin)
;;   (posn) pos2 : The second point; a point on the ray extending from the origin
;;
;; RETURN:
;;   The reference angle (in radians)
;;
;; EXAMPLES:
;;   (base-angle (make-posn 50 50) (make-posn (75 25)) = pi/4
;;   (base-angle (make-posn 50 50) (make-posn (50 100)) = pi/2
(define (base-angle pos1 pos2)
  (cond
    ;; First we make sure that the two points are not vertical with each other
    ;; Vertical points make atan barf.  Actually, they make / barf, too.
    [(= (- (posn-x pos2) (posn-x pos1)) 0) (/ pi 2)]
    ;; Ugly bit coming up (though there are far worse later).  Basically, we determine the
    ;; tangent of the angle by finding the ratio of the relative y and x values.  We're using
    ;; absolute values because we don't much care about what quadrant it ends up in.  Once we
    ;; have the tangent, we take it's inverse to find the angle, and Tada!
    [else (atan (/ (abs (- (posn-y pos2) (posn-y pos1))) (abs (- (posn-x pos2) (posn-x pos1)))))]
    )
  )

;; FUNCTION distance
;;
;; SYNTAX:
;;   (distance pos1 pos2)
;;
;; DESCRIPTION:
;;   Determines the distance between points A and B using the Pythagorean Theorem.
;;
;; INPUT:
;;   (posn) pos1 : Point A
;;   (posn) pos2 : Point B
;;
;; RETURN:
;;   The distance between them
;;
;; EXAMPLES:
;;   (distance (make-posn 0 0) (make-posn 0 50)) = 50
;;   (distance (make-posn 50 50) (make-posn 53 54)) = 5
(define (distance pos1 pos2)
  ;; Another ugly bugger.  But it's just the pythagorean theorem.  Honest.
  (sqrt (+ (expt (- (posn-x pos1) (posn-x pos2)) 2) (expt (- (posn-y pos1) (posn-y pos2)) 2))))

;; FUNCTION what-angle
;;
;; SYNTAX:
;;   (what-angle pos1 pos2)
;;
;; DESCRIPTION:
;;   Finds the real angle, like base-angle, but including quadrant information.  That is,
;;   now it will return a number from 0 to 2pi.
;;
;; INPUT:
;;   (posn) pos1 : The first point (the origin)
;;   (posn) pos2 : The second point; a point on the ray extending from the origin
;;
;; RETURN
;;   Number from 0 to 2pi - the angle that between the x-axis and the ray
;;
;; EXAMPLES:
;;   (what-angle (make-posn 50 50) (make-posn (75 25)) = pi/4
;;   (what-angle (make-posn 50 50) (make-posn (50 100)) = 3pi/2
(define (what-angle pos1 pos2)
  (cond
    [(>= (- (posn-y pos2) (posn-y pos1)) 0)
      (cond
        [(> (- (posn-x pos2) (posn-x pos1)) 0) (base-angle pos1 pos2)]        ;; First quadrant
        [else (- pi (base-angle pos1 pos2))]                                  ;; Second quadrant
        )]
    [else
      (cond
        [(< (- (posn-x pos2) (posn-x pos1)) 0) (+ pi (base-angle pos1 pos2))] ;; Third quadrant
        [else (- (* 2 pi) (base-angle pos1 pos2))]                            ;; Fourth quadrant
        )
      ]
    )
  )

;; FUNCTION spiral-next-pos
;;
;; SYNTAX:
;;   (spiral-next-pos center-pos end-pos step-width decay)
;;
;; DESCRIPTION
;;   Determines the next point in the spiral... useful for drawing the lines to draw the
;;   spiral itself.  Actually, it finds the next step in a circle and subtracts an
;;   appropriate amount for decay, giving a net-effect of a spiral.
;; 
;; INPUT:
;;   (posn) center-pos  : The centerpoint of the spiral
;;   (posn) end-pos     : The point from which we're trying to find the next point
;;   (num)  step-width  : The number of radians each step should be.  Smaller numbers mean 
;;                          a finer line.
;;   (num)  decay       : The amount of decay each step.  It ranges from zero to one, with
;;                          0 being a straight line to the middle and 1 being a circle
;;
;; RETURN:
;;   A posn with the next place for the line.
;;
;; EXAMPLES:
;;   (spiral-next-pos (make-posn 500 500) (make-posn 500 750) (/ pi 8) 0.99)
(define (spiral-next-pos center-pos end-pos step-width decay)
  (make-posn
    (+ (* decay (distance center-pos end-pos) (cos (+ (what-angle center-pos end-pos) step-width))) (posn-x center-pos))
    (+ (* decay (distance center-pos end-pos) (sin (+ (what-angle center-pos end-pos) step-width))) (posn-y center-pos))
    )
  )

;; FUNCTION draw-spiral
;;
;; SYNTAX:
;;   (draw-spiral center-pos end-pos steps step-width decay color)
;;
;; DESCRIPTION:
;;   Draws a spiral
;;
;; INPUT:
;;   (posn) center-pos  : The centerpoint of the spiral
;;   (posn) end-pos     : An endpoint of the spiral
;;   (num) steps        : The number of steps that the spiral should take.  The more steps,
;;                          the further in the spiral will go.
;;   (num) step-width   : The width of each step in radians
;;   (num) decay        : How much decay toward the center happens with each step.  A value
;;                          of 0 means total decay; there will just be a single line to the
;;                          center.  A value of 1 means no decay; there will just be a circle.
;;                          A value in between will cause an inward spiral, and a value above
;;                          1 will cause an outward spiral.  A negative value will produce funky
;;                          results.
;;   (symbol) color     : The color of the spiral
;;
;; RETURNS:
;;   true (always)
;;
;; EXAMPLE:
;;   (draw-spiral (make-posn 500 500) (make-posn 500 1000) 500 (/ pi 8) 0.99 'Blue)
(define (draw-spiral center-pos end-pos steps step-width decay color)
  (cond
    [(<= steps 0) true]
    [else
      (and
        (draw-solid-line end-pos (spiral-next-pos center-pos end-pos step-width decay) color)
        (draw-spiral center-pos (spiral-next-pos center-pos end-pos step-width decay) (- steps 1) step-width decay color)
        )
      ]
    )
  )

;; FUNCTION star-next-pos
;;
;; SYNTAX:
;;   (star-next-pos center-pos end-pos step-width decay)
;;
;; DESCRIPTION
;;   Determines the next point in the star... useful for drawing the lines to draw the
;;   star itself.
;; 
;; INPUT:
;;   (posn) center-pos  : The centerpoint of the spiral
;;   (posn) end-pos     : The point from which we're trying to find the next point
;;   (num)  step-width  : The number of radians each step should be.  Smaller numbers mean 
;;                          a finer line.
;;   (num)  decay       : The amount of decay each step.  It ranges from zero to one, with
;;                          0 being a straight line to the middle and 1 being a circle
;;
;; RETURN:
;;   A posn with the next place for the line.
;;
;; EXAMPLES:
;;   (star-next-pos (make-posn 500 500) (make-posn 500 750) (/ pi 8) 0.99)
(define (star-next-pos center-pos end-pos step-width decay)
  (make-posn
   ;; This is basically the same as spiral-next-pos, except it sends it to the opposite side.
   ;; Makes for some interesting results, including the double spiral and star.
    (- (posn-x center-pos) (* decay (distance center-pos end-pos) (cos (+ (what-angle center-pos end-pos) step-width))))
    (- (posn-y center-pos) (* decay (distance center-pos end-pos) (sin (+ (what-angle center-pos end-pos) step-width))))
    )
  )

;; FUNCTION draw-double-spiral
;;
;; SYNTAX:
;;   (draw-double-spiral center-pos end-pos steps step-width decay color1 color2)
;;
;; DESCRIPTION:
;;   Draws two dashed spirals, one inside the other.
;;
;; INPUT:
;;   (posn) center-pos  : The centerpoint of the spiral
;;   (posn) end-pos     : An endpoint of the spiral 
;;   (num) steps        : The number of steps that the star should take.  The more steps,
;;                          the further in the star will go.
;;   (num) step-width   : The width of each step in radians
;;   (num) decay        : How much decay toward the center happens with each step.  A value
;;                          of 0 means total decay; there will just be a single line to the
;;                          center.  A value of 1 means no decay; there will just be a circle.
;;                          A value in between will cause an inward spiral, and a value above
;;                          1 will cause an outward spiral.  A negative value will produce funky
;;                          results.
;;   (symbol) color1    : The color of the outside spiral.
;;   (symbol) color2    : The color of the inside spiral.
;;
;; RETURNS:
;;   true (always)
;;
;; EXAMPLE:
;;   (draw-double-spiral (make-posn 500 500) (make-posn 500 1000) 500 (/ pi 8) 0.99 'Blue)
;;
;; NOTES:
;;   This code involves something of a hack between the star code and the spiral code, and
;;   as such, the function names within are a bit odd.  Understanding exactly what is happening
;;   may be slightly confusing, but rest-assured, it works.  Comprehension of what is happening
;;   becomes significantly easier when the function is played with and values are inserted.
(define (draw-double-spiral center-pos end-pos steps step-width decay color1 color2)
  (cond
    [(<= steps 0) true]
    [else
      (and
        (draw-solid-line end-pos (spiral-next-pos center-pos end-pos step-width decay) color1)
        (draw-double-spiral center-pos (star-next-pos center-pos end-pos step-width decay) (- steps 1) step-width decay color2 color1)
        )
      ]
    )
  )

;; FUNCTION draw-crazy-star
;;
;; SYNTAX:
;;   (draw-crazy-star center-pos end-pos steps step-width decay color1 color2)
;;
;; DESCRIPTION:
;;   Draws a peculiar star in either one or two colors, of a user-chosen number of points,
;;   and optionally changing point-length.  The end result can be a truly funky star that
;;   cannot really be described, but instead needs to be seen.  But, if you refuse to
;;   try it out, just take this to heart: It's just plain odd.
;;
;; INPUT:
;;   (posn) center-pos  : The centerpoint of the star
;;   (posn) end-pos     : An endpoint of the star
;;   (num) steps        : The number of steps that the star should take.  The more steps,
;;                          the further in the star will go.
;;   (num) step-width   : The width of each step in radians
;;   (num) decay        : How much decay toward the center happens with each step.  A value
;;                          of 0 means total decay; there will just be a single line to the
;;                          center.  A value of 1 means no decay; there will just be a circle.
;;                          A value in between will cause an inward spiral with the points, and a
;;                          value above 1 will cause an outward spiral.  A negative value will
;;                          produce funky results.
;;   (symbol) color1    : One star color.
;;   (symbol) color2    : The other color (not necessarily different).
;;
;; RETURNS:
;;   true (always)
;;
;; EXAMPLE:
;;   (draw-crazy star (make-posn 500 500) (make-posn 500 1000) 500 (/ pi 8) 0.99 'Blue)
(define (draw-crazy-star center-pos end-pos steps step-width decay color1 color2)
  (cond
    [(<= steps 0) true]
    [else
      (and
        (draw-solid-line end-pos (star-next-pos center-pos end-pos step-width decay) color1)
        (draw-crazy-star center-pos (star-next-pos center-pos end-pos step-width decay) (- steps 1) step-width decay color2 color1)
        )
      ]
    )
  )

;(start 400 200)
;(draw-spiral (make-posn 100 100) (make-posn 100 200) 800 (/ pi 4) 0.99 'Blue)
;(draw-spiral (make-posn 300 100) (make-posn 300 200) 800 (/ pi 8) 0.99 'Blue)
;(draw-crazy-star (make-posn 100 100) (make-posn 100 200) 500 (/ pi 8) 0.99 'Blue 'Red)
;(draw-crazy-star (make-posn 300 100) (make-posn 300 200) 500 (/ pi 4) 0.99 'Green 'Yellow)