;; fractal trees and forest ;; Some geometric stuff used to determine the endpoints of lines. ;; distance between 2 points (define (dist p1 p2) (sqrt (+ (expt (- (posn-x p1) (posn-x p2)) 2) (expt (- (posn-y p1) (posn-y p2)) 2)))) ;; ;; given three points p0, p1 and p2, rotate p2 about p1 relative to ;; the angle determined by the direction freom p0 to p1. (define (rotate p0 p1 p2 angle) (local ((define hyp01 (dist p0 p1)) (define hyp12 (dist p1 p2)) (define angle01 (atan (- (posn-y p1) (posn-y p0)) (- (posn-x p1) (posn-x p0))))) (make-posn (+ (posn-x p1) (* hyp12 (cos (+ angle01 angle)))) (+ (posn-y p1) (* hyp12 (sin (+ angle01 angle))))))) (define PI 3.141593) ;; mid-point : posn posn -> posn ;; to compute the mid-point between a-posn and b-posn (define (mid-point a-posn b-posn) (make-posn (mid (posn-x a-posn) (posn-x b-posn)) (mid (posn-y a-posn) (posn-y b-posn)))) ;; mid : number number -> number ;; to compute the average of x and y (define (mid x y) (/ (+ x y) 2)) ;; draw-recur-line draws a single tree recursively ;; threshold is when individual line length is < 5. (define (draw-recur-line start angle len color) (local ((define end (make-posn (+ (posn-x start) (* len (cos angle))) (+ (posn-y start) (* len (sin angle)))))) (cond [(< len 5) (draw-solid-line start end color)] [else (local ((define p1 (make-posn (+ (posn-x start) (/ (- (posn-x end) (posn-x start)) 3)) (+ (posn-y start) (/ (- (posn-y end) (posn-y start)) 3)))) (define p2 (make-posn (+ (posn-x start) (* (- (posn-x end) (posn-x start)) 2/3)) (+ (posn-y start) (* (- (posn-y end) (posn-y start)) 2/3)))) (define pmid (mid-point start end))) (and (draw-solid-line start end color) (draw-recur-line p1 (- angle (/ PI 8)) (/ len 2) color) (draw-recur-line pmid (+ angle (/ PI 6)) (/ len 2) color) (draw-recur-line (mid-point start pmid) (+ angle (/ PI 5)) (/ len 2) color) (draw-recur-line p2 (+ angle (/ PI 8)) (/ len 2) color)))]))) ; test code ;(draw-recur-line (make-posn 200 200) (- 0 (/ PI 2)) 150 'black) ;; support for drawing a forest - a list of greens (define greens '( Chartreuse DarkGreen DarkOliveGreen ForestGreen Green GreenYellow LightGreen OliveDrab)) ;; LawnGreen LimeGreen ;; select a random color (define (random-color clist) (local ((define pos (random (length clist))) (define (select index list) (cond [(= index 0) (first list)] [ else (select (- index 1) (rest list))]))) (select pos clist))) ;; generate a random tree ;; tree must start in lower half (it must touch the ground!) (define (make-random-tree width) (local ((define height (+ (/ width 8) (random (/ width 2)))) (define x (+ 10 (random (- width 20)))) (define y (+ (/ width 2) (random (/ width 2)))) (define clr (random-color greens))) (list x y height clr))) ;; generate a bunch of random trees (define (random-trees n width) (cond [(= n 0) empty] [else (cons (make-random-tree width) (random-trees (- n 1) width))])) ;; sort trees according to how close they are to the observer ;; (greater y means closer to observer) (define (sort-trees trees) (local ((define (treecmp t1 t2) (< (second t1) (second t2)))) (quicksort trees treecmp))) (define (draw-trees trees) (cond [(empty? trees) true] [else (local ((define x (first (first trees))) (define y (second (first trees))) (define len (third (first trees))) (define color (fourth (first trees)))) (and (draw-recur-line (make-posn x y) (- 0 (/ PI 2)) len color) (draw-trees(rest trees))))])) (define WINDOWSIZE 600) (define trees (sort-trees (random-trees 40 WINDOWSIZE))) (start WINDOWSIZE WINDOWSIZE) (draw-solid-rect (make-posn 0 0) WINDOWSIZE (/ WINDOWSIZE 2) 'SkyBlue) (draw-solid-rect (make-posn 0 (/ WINDOWSIZE 2)) WINDOWSIZE (/ WINDOWSIZE 2) 'DarkGoldenRod) (draw-solid-disk (make-posn 50 50) 60 'Gold) (draw-trees trees)