351 lines
13 KiB
Racket
351 lines
13 KiB
Racket
(module value-turtles mzscheme
|
|
(require mzlib/math
|
|
mzlib/class
|
|
mred
|
|
(all-except mzlib/list merge)
|
|
mzlib/struct)
|
|
|
|
(provide turtles move draw turn turn/radians merge clean)
|
|
|
|
;; a turtle is:
|
|
;; - (make-turtle x y theta)
|
|
;; where x, y, and theta are numbers
|
|
(define-struct turtle (x y angle))
|
|
(define turtle->vector (make-->vector turtle))
|
|
|
|
(define-struct offset (x y angle))
|
|
(define offset->vector (make-->vector offset))
|
|
|
|
;; a lines is:
|
|
;; - (list-of line)
|
|
(define-struct line (x1 y1 x2 y2 black?))
|
|
(define line->vector (make-->vector line))
|
|
|
|
;; a turtles is either
|
|
;; - (make-tmerge (list-of turtles/offset))
|
|
;; - (list-of turtle)
|
|
(define-struct tmerge (turtles))
|
|
(define tmerge->vector (make-->vector tmerge))
|
|
|
|
;; a turtles/offset is
|
|
;; - (make-turtles/offset turtles offset)
|
|
(define-struct turtles/offset (turtles offset))
|
|
(define turtles/offset->vector (make-->vector turtles/offset))
|
|
|
|
(define saved-turtle-snip% #f)
|
|
(define saved-turtles #f)
|
|
(define (vec->struc sexp)
|
|
(cond
|
|
[(pair? sexp) (cons (vec->struc (car sexp)) (vec->struc (cdr sexp)))]
|
|
[(vector? sexp)
|
|
(apply (case (vector-ref sexp 0)
|
|
[(struct:turtle) make-turtle]
|
|
[(struct:offset) make-offset]
|
|
[(struct:line) make-line]
|
|
[(struct:merge) make-tmerge]
|
|
[(struct:turtles/offset) make-turtles/offset]
|
|
[else (error 'vec->struc "unknown structure: ~s\n" sexp)])
|
|
(map vec->struc (vector-ref sexp 1)))]
|
|
[else sexp]))
|
|
|
|
(define (struc->vec sexp)
|
|
(cond
|
|
[(pair? sexp) (cons (struc->vec (car sexp)) (struc->vec (cdr sexp)))]
|
|
[(turtle? sexp) (vector 'struct:turtle (map struc->vec (vector->list (turtle->vector sexp))))]
|
|
[(offset? sexp) (vector 'struct:offset (map struc->vec (vector->list (offset->vector sexp))))]
|
|
[(line? sexp) (vector 'struct:line (map struc->vec (vector->list (line->vector sexp))))]
|
|
[(tmerge? sexp) (vector 'struct:tmerge (map struc->vec (vector->list (tmerge->vector sexp))))]
|
|
[(turtles/offset? sexp) (vector 'struct:turtles/offset (map struc->vec (vector->list (turtles/offset->vector sexp))))]
|
|
[else sexp]))
|
|
|
|
(define prim-read read)
|
|
(define prim-write write)
|
|
|
|
(define turtle-snip-class%
|
|
(class snip-class% ()
|
|
(define/override (read in-stream)
|
|
(unless saved-turtles
|
|
(error 'turtles "click execute before running the turtles"))
|
|
(let ([str (send in-stream get-string #f)])
|
|
(if (or (not str)
|
|
(string=? "" str))
|
|
(saved-turtles 150 150)
|
|
(let ([sexp (vec->struc (prim-read (open-input-string str)))])
|
|
(make-object saved-turtle-snip%
|
|
(first sexp)
|
|
(second sexp)
|
|
(third sexp)
|
|
(fourth sexp)
|
|
(fifth sexp))))))
|
|
(super-instantiate ())))
|
|
|
|
(define turtle-snipclass (make-object turtle-snip-class%))
|
|
|
|
(send turtle-snipclass set-classname "drscheme:turtle-snip")
|
|
(send turtle-snipclass set-version 1)
|
|
(send (get-the-snip-class-list) add turtle-snipclass)
|
|
|
|
(define pi/2 (/ pi 2))
|
|
(define (set-box/f b v) (when (box? b) (set-box! b v)))
|
|
(define icon-color "PURPLE")
|
|
(define icon-pen (send the-pen-list find-or-create-pen icon-color 1 'xor))
|
|
(define icon-brush (send the-brush-list find-or-create-brush icon-color 'xor))
|
|
(define blank-pen (send the-pen-list find-or-create-pen "BLACK" 1 'transparent))
|
|
(define w-pen (send the-pen-list find-or-create-pen "white" 1 'solid))
|
|
(define b-pen (send the-pen-list find-or-create-pen "black" 1 'solid))
|
|
(define w-brush (send the-brush-list find-or-create-brush "WHITE" 'solid))
|
|
|
|
(define empty-cache (make-offset 0 0 0))
|
|
|
|
(define turtle-snip%
|
|
(class snip%
|
|
(init-field width height turtles cache lines)
|
|
(define/public (get-lines) lines)
|
|
(define/public (get-turtles) turtles)
|
|
(define/public (get-cache) cache)
|
|
(define/public (get-width) width)
|
|
(define/public (get-height) height)
|
|
|
|
(define turtle-style 'triangle)
|
|
(define bitmap #f)
|
|
|
|
[define pl (make-object point% 0 0)]
|
|
[define pr (make-object point% 0 0)]
|
|
[define ph (make-object point% 0 0)]
|
|
[define points (list pl pr ph)]
|
|
(define (flip-icons dc dx dy)
|
|
(case turtle-style
|
|
[(triangle line)
|
|
(let* ([proc
|
|
(if (eq? turtle-style 'line)
|
|
(lambda (turtle)
|
|
(let ([x (turtle-x turtle)]
|
|
[y (turtle-y turtle)]
|
|
[theta (turtle-angle turtle)]
|
|
[size 2])
|
|
(send dc draw-line
|
|
(+ dx x)
|
|
(+ dy y)
|
|
(+ dx x (* size (cos theta)))
|
|
(+ dy y (* size (sin theta))))))
|
|
(lambda (turtle)
|
|
(let* ([x (turtle-x turtle)]
|
|
[y (turtle-y turtle)]
|
|
[theta (turtle-angle turtle)]
|
|
[long-size 20]
|
|
[short-size 7]
|
|
[l-theta (+ theta pi/2)]
|
|
[r-theta (- theta pi/2)])
|
|
(send ph set-x (+ dx x (* long-size (cos theta))))
|
|
(send ph set-y (+ dy y (* long-size (sin theta))))
|
|
(send pl set-x (+ dx x (* short-size (cos l-theta))))
|
|
(send pl set-y (+ dy y (* short-size (sin l-theta))))
|
|
(send pr set-x (+ dx x (* short-size (cos r-theta))))
|
|
(send pr set-y (+ dy y (* short-size (sin r-theta))))
|
|
(send dc draw-polygon points))))])
|
|
(if (eq? turtle-style 'line)
|
|
(send dc set-pen icon-pen)
|
|
(begin
|
|
(send dc set-pen blank-pen)
|
|
(send dc set-brush icon-brush)))
|
|
(for-each proc turtles)
|
|
(send dc set-pen b-pen))]
|
|
[else
|
|
(void)]))
|
|
|
|
(define (construct-bitmap)
|
|
(unless bitmap
|
|
(flatten)
|
|
(set! bitmap (make-object bitmap% width height))
|
|
(let* ([bitmap-dc (make-object bitmap-dc% bitmap)])
|
|
(send bitmap-dc clear)
|
|
(for-each (lambda (line)
|
|
(send bitmap-dc set-pen (if (line-black? line) b-pen w-pen))
|
|
(send bitmap-dc draw-line
|
|
(line-x1 line)
|
|
(line-y1 line)
|
|
(line-x2 line)
|
|
(line-y2 line)))
|
|
lines))))
|
|
|
|
(define/override (draw dc x y left top right bottom dx dy draw-caret)
|
|
(construct-bitmap)
|
|
(let ([old-pen (send dc get-pen)]
|
|
[old-brush (send dc get-brush)]
|
|
[old-clip (send dc get-clipping-region)])
|
|
(send dc set-pen b-pen)
|
|
(send dc set-brush w-brush)
|
|
(send dc draw-rectangle x y (+ width 2) (+ height 2))
|
|
(send dc set-clipping-rect (+ x 1) (+ y 1) width height)
|
|
(send dc draw-bitmap bitmap (+ x 1) (+ y 1))
|
|
(flip-icons dc (+ x 1) (+ y 1))
|
|
(send dc set-pen old-pen)
|
|
(send dc set-brush old-brush)
|
|
(send dc set-clipping-region old-clip)))
|
|
(define/override (get-extent dc x y w h descent space lspace rspace)
|
|
(set-box/f w (+ width 2))
|
|
(set-box/f h (+ height 2))
|
|
(set-box/f descent 0)
|
|
(set-box/f space 0)
|
|
(set-box/f lspace 0)
|
|
(set-box/f rspace 0))
|
|
|
|
(define/override (copy)
|
|
(make-object turtle-snip% width height turtles cache lines))
|
|
(define/override (write stream-out)
|
|
(let ([p (open-output-string)])
|
|
(prim-write (struc->vec (list width height turtles cache lines)) p)
|
|
(send stream-out put (get-output-string p))))
|
|
|
|
(define/public (flatten)
|
|
(letrec ([walk-turtles
|
|
(lambda (turtles offset sofar)
|
|
(cond
|
|
[(tmerge? turtles)
|
|
(let ([turtles/offsets (tmerge-turtles turtles)]
|
|
[ac (apply-offset cache)])
|
|
(foldl (lambda (turtles/offset sofar)
|
|
(walk-turtles (turtles/offset-turtles turtles/offset)
|
|
(combine-offsets
|
|
offset
|
|
(turtles/offset-offset turtles/offset))
|
|
sofar))
|
|
sofar
|
|
turtles/offsets))]
|
|
[else
|
|
(let ([f (apply-offset offset)])
|
|
(cond
|
|
[(null? sofar)
|
|
(map f turtles)]
|
|
[else
|
|
(foldl (lambda (t l) (cons (f t) l)) sofar turtles)]))]))])
|
|
(set! turtles (walk-turtles turtles cache null))
|
|
(set! cache empty-cache)))
|
|
|
|
(define (move-turtle dist)
|
|
(lambda (turtle)
|
|
(let ([x (turtle-x turtle)]
|
|
[y (turtle-y turtle)]
|
|
[theta (turtle-angle turtle)])
|
|
(make-turtle
|
|
(+ x (* dist (cos theta)))
|
|
(+ y (* dist (sin theta)))
|
|
theta))))
|
|
|
|
(define/public (draw-op d)
|
|
(flatten)
|
|
(let ([build-line
|
|
(lambda (turtle)
|
|
(let ([x (turtle-x turtle)]
|
|
[y (turtle-y turtle)]
|
|
[theta (turtle-angle turtle)])
|
|
(make-line
|
|
x
|
|
y
|
|
(+ x (* d (cos theta)))
|
|
(+ y (* d (sin theta)))
|
|
#t)))])
|
|
(make-object turtle-snip%
|
|
width
|
|
height
|
|
(map (move-turtle d) turtles)
|
|
cache
|
|
(foldl (lambda (turtle lines) (cons (build-line turtle) lines))
|
|
lines
|
|
turtles))))
|
|
(define/public (merge-op tvs)
|
|
(make-object turtle-snip%
|
|
width
|
|
height
|
|
(make-tmerge (map (lambda (tv) (make-turtles/offset
|
|
(send tv get-turtles)
|
|
(send tv get-cache)))
|
|
(cons this tvs)))
|
|
empty-cache
|
|
lines))
|
|
|
|
(define/public (move-op n)
|
|
(make-object turtle-snip%
|
|
width
|
|
height
|
|
turtles
|
|
(let* ([angle (offset-angle cache)]
|
|
[x (offset-x cache)]
|
|
[y (offset-y cache)]
|
|
[newx (+ x (* n (cos angle)))]
|
|
[newy (+ y (* n (sin angle)))])
|
|
(make-offset newx newy angle))
|
|
lines))
|
|
(define/public (turn-op d)
|
|
(make-object turtle-snip%
|
|
width
|
|
height
|
|
turtles
|
|
(make-offset (offset-x cache)
|
|
(offset-y cache)
|
|
(- (offset-angle cache)
|
|
d))
|
|
lines))
|
|
(define/public (clean-op)
|
|
(flatten)
|
|
(make-object turtle-snip%
|
|
width
|
|
height
|
|
null
|
|
empty-cache
|
|
lines))
|
|
(super-instantiate ())
|
|
(inherit set-snipclass)
|
|
(set-snipclass turtle-snipclass)))
|
|
|
|
(define apply-offset
|
|
(lambda (offset)
|
|
(let ([x (offset-x offset)]
|
|
[y (offset-y offset)]
|
|
[offset-angle (offset-angle offset)])
|
|
(lambda (turtle)
|
|
(let* ([angle (turtle-angle turtle)])
|
|
(let* ([c (cos angle)]
|
|
[s (sin angle)]
|
|
[rx (- (* x c) (* y s))]
|
|
[ry (+ (* y c) (* x s))])
|
|
(make-turtle (+ rx (turtle-x turtle))
|
|
(+ ry (turtle-y turtle))
|
|
(+ offset-angle angle))))))))
|
|
|
|
(define combine-offsets
|
|
(lambda (offset1 offset2)
|
|
(let ([answer ((apply-offset offset1)
|
|
(make-turtle
|
|
(offset-x offset2)
|
|
(offset-y offset2)
|
|
(offset-angle offset2)))])
|
|
(make-offset
|
|
(turtle-x answer)
|
|
(turtle-y answer)
|
|
(turtle-angle answer)))))
|
|
|
|
(define turtles
|
|
(case-lambda
|
|
[(width height x y theta)
|
|
(make-object turtle-snip%
|
|
width height
|
|
(list (make-turtle x y theta))
|
|
empty-cache
|
|
null)]
|
|
[(width height)
|
|
(turtles width height
|
|
(quotient width 2)
|
|
(quotient height 2)
|
|
0)]))
|
|
|
|
(define (move d tv) (send tv move-op d))
|
|
(define (draw d tv) (send tv draw-op d))
|
|
(define (turn/radians d tv) (send tv turn-op d))
|
|
(define (turn d tv) (turn/radians (* (/ d 360) 2 pi) tv))
|
|
(define (merge tv . tvs) (send tv merge-op tvs))
|
|
(define (clean tv) (send tv clean-op))
|
|
|
|
(set! saved-turtle-snip% turtle-snip%)
|
|
(set! saved-turtles turtles))
|