racket/collects/plot/plot3d/snip.rkt
2012-02-25 22:47:09 -07:00

143 lines
4.9 KiB
Racket

#lang racket/base
(require racket/gui/base racket/class racket/match unstable/parameter-group
"../common/snip.rkt"
"../common/math.rkt"
"../common/worker-thread.rkt"
"../common/parameters.rkt")
(provide 3d-plot-snip% make-3d-plot-snip)
(define show-rotate-message? #t)
(struct draw-command (animating? angle altitude width height) #:transparent)
(define (clamp x mn mx) (min* (max* x mn) mx))
(define 3d-plot-snip%
(class plot-snip%
(init init-bm saved-plot-parameters)
(init-field make-bm angle altitude width height)
(inherit set-bitmap get-bitmap
get-saved-plot-parameters
set-message stop-message set-message-center reset-message-timeout
update-thread-running? set-update
get-left-down-here?)
(super-make-object init-bm saved-plot-parameters)
(define/override (copy)
(make-object this%
(get-bitmap) (get-saved-plot-parameters)
make-bm angle altitude width height))
(define mouse-x 0)
(define mouse-y 0)
(define left-click-x 0)
(define left-click-y 0)
(define last-angle angle)
(define last-altitude altitude)
(define (set-angle!)
(define degrees-per-pixel (/ 180 (send (get-bitmap) get-width)))
(define dx (- mouse-x left-click-x))
(set! angle (real-modulo (+ last-angle (* dx degrees-per-pixel)) 360)))
(define (set-altitude!)
(define degrees-per-pixel (/ 180 (send (get-bitmap) get-height)))
(define dy (- mouse-y left-click-y))
(set! altitude (clamp (+ last-altitude (* dy degrees-per-pixel)) 0 90)))
(define (start-update-thread animating?)
(send this start-update-thread
(λ () (make-worker-thread
(match-lambda
[(draw-command animating? angle altitude width height)
(make-bm animating? angle altitude width height)])))
(λ (animating?) (draw-command animating? angle altitude width height))
(λ (rth)
(define new-bm (worker-thread-try-get rth))
(cond [(is-a? new-bm bitmap%)
(set-bitmap new-bm)
(when (not (and (= last-angle angle)
(= last-altitude altitude)))
(set-angles-message))
#t]
[else #f]))
animating?))
(define (set-angles-message)
(set-message (format "angle = ~a\naltitude = ~a"
(number->string (inexact->exact (round angle)))
(number->string (inexact->exact (round altitude))))
#:refresh? #f))
(define (set-click-message)
(when show-rotate-message?
(set-message "Click and drag to rotate")))
(define (on-left-down)
(set! left-click-x mouse-x)
(set! left-click-y mouse-y)
(set! last-angle angle)
(set! last-altitude altitude)
(set-angles-message)
(start-update-thread #t)
(set-update #t))
(define (on-left-up)
(when (get-left-down-here?)
(set! last-angle angle)
(set! last-altitude altitude)
(when (update-thread-running?)
(start-update-thread #f)
(set-update #t))))
(define (on-motion evt last-mouse-x last-mouse-y)
(cond [(get-left-down-here?)
(when (not (and (= last-mouse-x mouse-x)
(= last-mouse-y mouse-y)))
(set! show-rotate-message? #f)
(set-angle!)
(set-altitude!)
(unless (update-thread-running?)
(start-update-thread #t))
(set-update #t))]
[(and (not (send evt get-left-down))
(<= 0 mouse-x (send (get-bitmap) get-width))
(<= 0 mouse-y (send (get-bitmap) get-height)))
(set-click-message)]))
(define/override (on-event dc x y editorx editory evt)
(define evt-type (send evt get-event-type))
(define last-mouse-x mouse-x)
(define last-mouse-y mouse-y)
(set! mouse-x (- (send evt get-x) x))
(set! mouse-y (- (send evt get-y) y))
(case evt-type
[(left-down) (on-left-down)]
[(left-up) (on-left-up)]
[(motion) (on-motion evt last-mouse-x last-mouse-y)])
(super on-event dc x y editorx editory evt))
(define/override (resize w h)
(when (not (and (= w width) (= h height)))
(set! width w)
(set! height h)
(set-message-center (* 1/2 w) (* 1/2 h))
(stop-message)
(when (not (update-thread-running?))
(start-update-thread #t))
(set-update #t))
#f)
))
(define (make-3d-plot-snip
init-bm saved-plot-parameters
make-bm angle altitude width height)
(make-object 3d-plot-snip%
init-bm saved-plot-parameters
make-bm angle altitude width height))