;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;             U. C. Berkeley            ;;
;;      EECS Computer Science Division   ;;
;;         CS3 Lecture 22 Answers        ;;
;;               (Fractals)              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;
;; CONSTANTS (to make our life easier)
;;;;;;;;;;;;

;; Window borders

(define *L* -200)
(define *R*  200)
(define *D* -200)
(define *U*  200)

;; Numerical constants we'll use often

(define *sqrt3*   (sqrt 3))
(define *sqrt3/2* (/ *sqrt3* 2))

;;;;;;;;;;;;
;; DRAW-LINE
;;;;;;;;;;;;

(define (draw-line x1 y1 x2 y2)
  (position-pen x1 y1)
  (draw-line-to x2 y2))

;;;;;;;;;;;;;;;;;;;;
;; SIERPINSKI-SQUARE
;;;;;;;;;;;;;;;;;;;;

(define (square L R D U)
  (position-pen L D)
  (draw-line-to R D)
  (draw-line-to R U)
  (draw-line-to L U)
  (draw-line-to L D))

(define (sierpinski-square L R D U n)
  (if (= n 0) 
      (square L R D U)
      (let ((Lish (+ L (* (- R L) 1/3)))
            (Rish (+ L (* (- R L) 2/3)))
            (Dish (+ D (* (- U D) 1/3)))
            (Uish (+ D (* (- U D) 2/3))))
        (sierpinski-square L Lish D Dish (- n 1))
        (sierpinski-square Rish R D Dish (- n 1))
        (sierpinski-square L Lish Uish U (- n 1))
        (sierpinski-square Rish R Uish U (- n 1))
        (sierpinski-square Lish Rish Dish Uish (- n 1)) )))

;; DEMO-SIERPINSKI-SQUARE

(define (demo-sierpinski-square)
  (demo-sierpinski-square-helper 0 5))
(define (demo-sierpinski-square-helper n n-finish)
  (if (> n n-finish)
      'done
      (begin
       (display `(Hit enter to display frame ,n of ,n-finish))
       (read)
       (clear-graphics)
       (sierpinski-square *L* (- *R* 1) (+ *D* 1) *U* n)
       (demo-sierpinski-square-helper (+ 1 n) n-finish))))

;; (clear-graphics)
;; (sierpinski-square *L* *R* *D* *U* 5)
;; (demo-sierpinski-square)

;;;;;;;;;;;;;;;;;;;;;;
;; SIERPINSKI TRIANGLE
;;;;;;;;;;;;;;;;;;;;;;

(define (triangle L R D U)
  (position-pen L D)
  (draw-line-to R D)
  (draw-line-to (/ (+ L R) 2) U)
  (draw-line-to L D))

(define (sierpinski-triangle L R D U n)
  (if (= n 0)
      (triangle L R D U)
      (let ((LR (/ (+ L R) 2))
            (Lish (+ L (* (- R L) 1/4)))
            (Rish (+ L (* (- R L) 3/4)))
            (DU (/ (+ D U) 2)))
        (sierpinski-triangle L LR D DU (- n 1))
        (sierpinski-triangle LR R D DU (- n 1))
        (sierpinski-triangle Lish Rish DU U (- n 1)) )))


;; DEMO-SIERPINSKI-TRIANGLE

(define (demo-sierpinski-triangle)
  (demo-sierpinski-triangle-helper 0 7))
(define (demo-sierpinski-triangle-helper n n-finish)
  (if (> n n-finish)
      'done
      (begin
       (display `(Hit enter to display frame ,n of ,n-finish))
       (read)
       (clear-graphics)
       (sierpinski-triangle *L* *R* (- *U* (* (- *R* *L*) *sqrt3/2*)) *U* n)
       (demo-sierpinski-triangle-helper (+ 1 n) n-finish))))

;; (clear-graphics)
;; (sierpinski-triangle *L* *R* (- *U* (* (- *R* *L*) *sqrt3/2*)) *U* 8)
;; (demo-sierpinski-triangle)

;;;;;;;
;; TREE
;;;;;;;

(define (tree L R D U n)
  (if (= n 1)
      (draw-tree L R D U)
      (let ((DUn (/ (+ (* (- n 1) U) D) n))
            (LR (/ (+ L R) 2)))
        (draw-tree L R DUn U)
        (tree L LR D DUn (- n 1))
        (tree LR R D DUn (- n 1)))))

(define (draw-tree L R D U)
  (let ((LLR (/ (+ (* 3 L) R) 4))
        (LR (/ (+ L R) 2))
        (LRR (/ (+ L (* 3 R)) 4)))
    (position-pen LLR D)
    (draw-line-to LR U)
    (draw-line-to LRR D)))

;; DEMO-TREE

(define (demo-tree)
  (demo-tree-helper 1 7))
(define (demo-tree-helper n n-finish)
  (if (> n n-finish)
      'done
      (begin
       (display `(Hit enter to display frame ,n of ,n-finish))
       (read)
       (clear-graphics)
       (tree *L* *R* *D* *U* n)
       (demo-tree-helper (+ 1 n) n-finish))))

;; (clear-graphics)
;; (tree *L* *R* *D* *U* 7)
;; (demo-tree)

;;;;;;;;
;; PEANO
;;;;;;;;

(define (peano x1 y1 x2 y2 n)
  (if (= n 0)
      (draw-line x1 y1 x2 y2)
      (let* ((x1L (+ x1 (* (- x2 x1) 1/3)))
             (y1L (+ y1 (* (- y2 y1) 1/3)))
             (x2L (+ x1 (* (- x2 x1) 2/3)))
             (y2L (+ y1 (* (- y2 y1) 2/3)))
             (x1U (/ (- (+ x2L x1 y1) y2L) 2))
             (y1U (/ (- (+ x2L y1 y2L) x1) 2))
             (x2U (/ (- (+ x2 x1L y1L) y2) 2))
             (y2U (/ (- (+ x2 y1L y2) x1L) 2))
             (x1D (/ (- (+ x2L x1 y2L) y1) 2))
             (y1D (/ (- (+ x1 y1 y2L) x2L) 2))
             (x2D (/ (- (+ x2 x1L y2) y1L) 2))
             (y2D (/ (- (+ x1L y1L y2) x2) 2)))
        (peano x1 y1 x1L y1L (- n 1))
        (peano x1L y1L x2L y2L (- n 1))
        (peano x2L y2L x2 y2 (- n 1))
        (peano x1L y1L x1U y1U (- n 1))
        (peano x1U y1U x2U y2U (- n 1))
        (peano x2U y2U x2L y2L (- n 1))
        (peano x1L y1L x1D y1D (- n 1))
        (peano x1D y1D x2D y2D (- n 1))
        (peano x2D y2D x2L y2L (- n 1)) )))
        
;; DEMO PEANO

(define (demo-peano)
  (demo-peano-helper 0 4))
(define (demo-peano-helper n n-finish)
  (if (> n n-finish)
      'done
      (begin
       (display `(Hit enter to display frame ,n of ,n-finish))
       (read)
       (clear-graphics)
       (peano *L* (/ (+ *U* *D*) 2) *R* (/ (+ *U* *D*) 2) n)
       (demo-peano-helper (+ 1 n) n-finish))))

;; (clear-graphics)
;; (peano *L* (/ (+ *U* *D*) 2) *R* (/ (+ *U* *D*) 2) 4)
;; (demo-peano)

;;;;;;;;;;;;;;;;;;;
;; C-CURVE / DRAGON
;;;;;;;;;;;;;;;;;;;

(define (c-curve/dragon x1 y1 x2 y2 level dragon?)
  (if (= level 0)
      (draw-line x1 y1 x2 y2)
      (let ((xm (/ (- (+ x2 x1 y1) y2) 2))
            (ym (/ (- (+ x2 y1 y2) x1) 2)))
        (c-curve/dragon x1 y1 xm ym (- level 1) dragon?)
        (if dragon?
            (c-curve/dragon x2 y2 xm ym (- level 1) dragon?)
            (c-curve/dragon xm ym x2 y2 (- level 1) dragon?)))))

;; DEMO C-CURVE AND DRAGON

(define (demo-c-curve)
  (demo-c-curve/dragon-helper 0 13 #f))
(define (demo-dragon)
  (demo-c-curve/dragon-helper 0 13 #t))
(define (demo-c-curve/dragon-helper n n-finish dragon?)
  (if (> n n-finish)
      'done
      (begin
       (display `(Hit enter to display frame ,n of ,n-finish))
       (read)
       (clear-graphics)
       (c-curve/dragon (/ *L* 2) (/ *D* 4) (/ *R* 2) (/ *D* 4) n dragon?)
       (demo-c-curve/dragon-helper (+ 1 n) n-finish dragon?))))

;; (clear-graphics)
;; (c-curve/dragon (/ *L* 2) (/ *D* 4) (/ *R* 2) (/ *D* 4) 12 #t)
;; (demo-c-curve)
;; (demo-dragon)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; KOCH (SNOWFLAKE) / ANTI-KOCH (ANTI-SNOWFLAKE) CURVE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (koch-edge x1 y1 x2 y2 n)
  (if (= n 0)
      (draw-line x1 y1 x2 y2)
      (let* ((x11 (+ x1 (* (- x2 x1) 1/3)))
             (y11 (+ y1 (* (- y2 y1) 1/3)))
             (x22 (+ x1 (* (- x2 x1) 2/3)))
             (y22 (+ y1 (* (- y2 y1) 2/3)))
             (xm (/ (- (+ x22 x11 (* *sqrt3* y11)) (* *sqrt3* y22)) 2))
             (ym  (/ (- (+ (* *sqrt3* x22) y11 y22) (* *sqrt3* x11)) 2)))
            (koch-edge x1 y1 x11 y11 (- n 1))
            (koch-edge x11 y11 xm ym (- n 1))
            (koch-edge xm ym x22 y22 (- n 1))
            (koch-edge x22 y22 x2 y2 (- n 1)) )))

(define (koch n invert?)
  (let* ((yBottom *D*)             ;; Bottom window border
         (yTop *U*)                ;; Top window border
         (width (/ (* 3/4 (- yTop yBottom)) *sqrt3/2*))
         (xLeft (- (/ width 2)))   ;; Left window border
         (xRight (/ width 2))      ;; Right window border
         (xMiddle (/ (+ xLeft xRight) 2)) ;; midway from xLeft,xRight
         (yTriangleBottom (- yTop (* (- xRight xLeft) *sqrt3/2*))))
    (if invert?
        (begin   ;; Go counter-clockwise
         (koch-edge xLeft yTriangleBottom xRight yTriangleBottom n)
         (koch-edge xRight yTriangleBottom xMiddle yTop n)
         (koch-edge xMiddle yTop xLeft yTriangleBottom n))
        (begin   ;; Go clockwise
         (koch-edge xLeft yTriangleBottom xMiddle yTop n)
         (koch-edge xMiddle yTop xRight yTriangleBottom n)
         (koch-edge xRight yTriangleBottom xLeft yTriangleBottom n)))))

;; DEMO-KOCH

(define (demo-koch)
  (demo-koch-helper 0 5 #f))
(define (demo-koch-inverted)
  (demo-koch-helper 0 5 #t))
(define (demo-koch-helper n n-finish invert?)
  (if (> n n-finish)
      'done
      (begin
       (display `(Hit enter to display frame ,n of ,n-finish))
       (read)
       (clear-graphics)
       (koch n invert?)
       (demo-koch-helper (+ 1 n) n-finish invert?))))

;; (clear-graphics)
;; (koch 5 #t)
;; (demo-koch)
;; (demo-koch-inverted)