#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) (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 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) 20))) (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))