racket/collects/plot/plot3d/snip.rkt
Neil Toronto 6b39863f1c Source reorg
Added non-rendering plot elements
Optimizations
2011-11-10 12:59:41 -07:00

151 lines
5.5 KiB
Racket

#lang racket/base
(require racket/gui/base racket/class racket/match racket/bool racket/async-channel
"../common/gui.rkt"
"../common/math.rkt"
"area.rkt")
(provide 3d-plot-snip% make-3d-plot-snip)
(define update-delay 33) ; about 30 fps (just over)
(struct render-thread (state command-channel response-channel thread) #:mutable #:transparent)
(struct draw-command (angle altitude animating?) #:transparent)
(struct copy-command () #:transparent)
(define (make-render-thread make-bm)
(define com-ch (make-channel))
(define res-ch (make-async-channel))
(define th
(thread
(λ ()
(let loop ()
(match (channel-get com-ch)
[(draw-command angle altitude animating?)
(define bm (with-handlers ([exn? (λ (e) (async-channel-put res-ch e))])
(make-bm angle altitude animating?)))
(async-channel-put res-ch bm)]
[(copy-command) (async-channel-put res-ch (make-render-thread make-bm))])
(loop)))))
(render-thread 'wait com-ch res-ch th))
(define (render-thread-get-bitmap r)
(match-define (render-thread state com-ch res-ch th) r)
(define res (async-channel-get res-ch))
(set-render-thread-state! r 'wait)
(if (exn? res) (raise res) res))
(define (render-thread-try-get-bitmap r)
(match-define (render-thread state com-ch res-ch th) r)
(define res (async-channel-try-get res-ch))
(when res (set-render-thread-state! r 'wait))
(if (exn? res) (raise res) res))
(define (render-thread-wait r)
(match-define (render-thread state com-ch res-ch th) r)
(when (symbol=? state 'drawing)
(render-thread-get-bitmap r)))
(define (render-thread-draw r angle altitude animating?)
(render-thread-wait r)
(match-define (render-thread state com-ch res-ch th) r)
(channel-put com-ch (draw-command angle altitude animating?))
(set-render-thread-state! r 'drawing))
(define (render-thread-copy r)
(render-thread-wait r)
(match-define (render-thread state com-ch res-ch th) r)
(channel-put com-ch (copy-command))
(async-channel-get res-ch))
(define (clamp x mn mx) (min* (max* x mn) mx))
(define 3d-plot-snip%
(class image-snip%
(init-field make-bm angle altitude
[bm (make-bm angle altitude #f)]
[rth (make-render-thread make-bm)])
(inherit set-bitmap)
(super-make-object bm)
(define width (send bm get-width))
(define height (send bm get-height))
(define click-x 0)
(define click-y 0)
(define drag-x 0)
(define drag-y 0)
(define (new-angle) (real-modulo (+ angle (* (- drag-x click-x) (/ 180 width))) 360))
(define (new-altitude) (clamp (+ altitude (* (- drag-y click-y) (/ 180 height))) 0 90))
(define draw? #t)
(define timer #f)
(define ((update animating?))
(define can-draw? (case (render-thread-state rth)
[(wait) #t]
[(drawing) (define new-bm (render-thread-try-get-bitmap rth))
(cond [(is-a? new-bm bitmap%) (set! bm new-bm)
(set-bitmap bm)
#t]
[else #f])]))
(when (and draw? can-draw?)
(set! draw? #f)
(render-thread-draw rth (new-angle) (new-altitude) animating?)))
(define (stop-timer)
(when timer
(send timer stop)
(set! timer #f)))
(define (start-timer)
(stop-timer)
(set! timer (make-object timer% (update #t) update-delay)))
(define/override (on-event dc x y editorx editory evt)
(case (send evt get-event-type)
[(left-down) (render-thread-wait rth)
(set! angle (new-angle))
(set! altitude (new-altitude))
(set! click-x (send evt get-x))
(set! click-y (send evt get-y))
(set! drag-x click-x)
(set! drag-y click-y)
(set! draw? #t)
(start-timer)]
[(left-up) (stop-timer)
(set! draw? #f)
(render-thread-wait rth)
(set! drag-x (send evt get-x))
(set! drag-y (send evt get-y))
(set! angle (new-angle))
(set! altitude (new-altitude))
(set! click-x 0)
(set! click-y 0)
(set! drag-x 0)
(set! drag-y 0)
(render-thread-draw rth angle altitude #f)
(define new-bm (render-thread-get-bitmap rth))
(when (is-a? new-bm bitmap%)
(set! bm new-bm)
(set-bitmap bm))]
[(motion) (when (and timer (send evt get-left-down))
(set! drag-x (send evt get-x))
(set! drag-y (send evt get-y))
(set! draw? #t))]))
(define/override (copy)
(make-object this% make-bm angle altitude bm (render-thread-copy rth)))
(define cross-cursor (make-object cursor% 'cross))
(define/override (adjust-cursor dc x y editorx editory evt) cross-cursor)
(send this set-flags (list* 'handles-events (send this get-flags)))))
;; make-3d-plot-snip : (real real real -> bitmap) real real -> 3d-plot-snip%
(define (make-3d-plot-snip make-bm angle altitude)
(make-object 3d-plot-snip% make-bm angle altitude))