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

217 lines
8.6 KiB
Racket

#lang racket/base
(require racket/gui/base racket/class racket/match racket/list racket/math unstable/parameter-group
"../common/snip.rkt"
"../common/plot-device.rkt"
"../common/math.rkt"
"../common/format.rkt"
"../common/ticks.rkt"
"../common/worker-thread.rkt"
"../common/parameters.rkt")
(provide 2d-plot-snip% make-2d-plot-snip)
(define update-delay 16)
(define show-zoom-message? #t)
(struct draw-command (animating? plot-bounds-rect width height) #:transparent)
(define 2d-plot-snip%
(class plot-snip%
(init init-bm saved-plot-parameters)
(init-field make-bm plot-bounds-rect area-bounds-rect area-bounds->plot-bounds width height)
(inherit set-bitmap get-bitmap
get-saved-plot-parameters
refresh
stop-message set-message reset-message-timeout
update-thread-running? set-update
get-left-down-here?)
(super-make-object init-bm saved-plot-parameters)
(define (set-message-center)
(match-define (vector x-mid y-mid) (rect-center area-bounds-rect))
(send this set-message-center x-mid y-mid))
(set-message-center)
(define/override (copy)
(make-object this%
(get-bitmap) (get-saved-plot-parameters)
make-bm plot-bounds-rect area-bounds-rect area-bounds->plot-bounds width height))
(define left-click-x 0)
(define left-click-y 0)
(define left-drag-x 0)
(define left-drag-y 0)
(define plot-bounds-rects empty)
(define (get-new-area-bounds-rect)
(rect-meet area-bounds-rect
(rect-inexact->exact
(vector (ivl left-click-x left-drag-x) (ivl left-click-y left-drag-y)))))
(define dragging? #f)
(define zoom-timer #f)
(define (set-zoom-timer)
(when (not zoom-timer)
(set! zoom-timer (make-object timer%
(λ ()
(set! zoom-timer #f)
(refresh))
update-delay #t))))
(define (set-click-message)
(when show-zoom-message?
(set-message "Click and drag to zoom\n Click to unzoom once")))
(define (zoom-or-unzoom)
(cond [dragging?
(set! dragging? #f)
(define new-rect (area-bounds->plot-bounds (get-new-area-bounds-rect)))
(cond [(and (rect-rational? new-rect) (not (rect-zero-area? new-rect)))
#;(printf "~a: new-plot-bounds-rect = ~v~n"
(current-milliseconds) new-rect)
(set! plot-bounds-rects (cons plot-bounds-rect plot-bounds-rects))
(set! plot-bounds-rect new-rect)
(update-plot)]
[else
(refresh)])]
[(not (empty? plot-bounds-rects))
(set! plot-bounds-rect (first plot-bounds-rects))
(set! plot-bounds-rects (rest plot-bounds-rects))
(set! show-zoom-message? #f)
(update-plot)]))
(define (start-update-thread animating?)
(send this start-update-thread
(λ () (make-worker-thread
(match-lambda
[(draw-command animating? plot-bounds-rect width height)
(make-bm animating? plot-bounds-rect width height)])))
(λ (animating?) (draw-command animating? plot-bounds-rect width height))
(λ (rth)
(define-values (new-bm new-area-bounds-rect new-area-bounds->plot-bounds)
(worker-thread-try-get rth (λ () (values #f #f #f))))
(cond [(is-a? new-bm bitmap%)
(set! area-bounds-rect new-area-bounds-rect)
(set! area-bounds->plot-bounds new-area-bounds->plot-bounds)
(set-bitmap new-bm)
(set-message-center)
#t]
[else #f]))
animating?))
(define (update-plot)
(start-update-thread #f)
(set-update #t))
(define/override (on-event dc x y editorx editory evt)
(define evt-type (send evt get-event-type))
(define mouse-x (- (send evt get-x) x))
(define mouse-y (- (send evt get-y) y))
(case evt-type
[(left-down) (set! left-click-x mouse-x)
(set! left-click-y mouse-y)
(set! left-drag-x mouse-x)
(set! left-drag-y mouse-y)
(set! dragging? #f)
(set-message #f)
(set-zoom-timer)]
[(left-up) (set! left-drag-x mouse-x)
(set! left-drag-y mouse-y)
(zoom-or-unzoom)]
[(motion) (cond [(get-left-down-here?) ; only #t if clicked on snip
(when (not (and (= left-drag-x mouse-x)
(= left-drag-y mouse-y)))
(set! left-drag-x mouse-x)
(set! left-drag-y mouse-y)
(set! dragging? #t)
(set-zoom-timer))]
[(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)])])
(super on-event dc x y editorx editory evt))
(define (draw-selection dc dc-x-min dc-y-min rect)
(when (and (rect-rational? rect) (not (rect-zero-area? rect)))
(define width (send (get-bitmap) get-width))
(define height (send (get-bitmap) get-height))
(define pd (make-object plot-device% dc dc-x-min dc-y-min width height))
(send pd reset-drawing-params #f)
(define select-color (get-highlight-background-color))
;; inside selection
(send pd set-pen select-color 1 'transparent)
(send pd set-brush select-color 'solid)
(send pd set-alpha 1/4)
(send pd draw-rect rect)
;; selection border
(send pd set-minor-pen)
(send pd set-brush select-color 'transparent)
(send pd set-alpha 3/4)
(send pd draw-rect rect)
;; format side labels
(match-define (vector (ivl x-min x-max) (ivl y-min y-max)) plot-bounds-rect)
(match-define (vector (ivl new-x-min new-x-max) (ivl new-y-min new-y-max))
(area-bounds->plot-bounds rect))
(match-define (list new-x-min-str new-x-max-str)
(format-tick-labels (plot-x-ticks) x-min x-max (list new-x-min new-x-max)))
(match-define (list new-y-min-str new-y-max-str)
(format-tick-labels (plot-y-ticks) y-min y-max (list new-y-min new-y-max)))
;; draw side labels
(match-define (vector (ivl new-area-x-min new-area-x-max)
(ivl new-area-y-min new-area-y-max))
rect)
(define new-area-x-mid (* 1/2 (+ new-area-x-min new-area-x-max)))
(define new-area-y-mid (* 1/2 (+ new-area-y-min new-area-y-max)))
(send pd set-alpha 1)
(send pd draw-text new-x-min-str (vector new-area-x-min new-area-y-mid)
'center (* 1/2 pi) #:outline? #t)
(send pd draw-text new-x-max-str (vector new-area-x-max new-area-y-mid)
'center (* 1/2 pi) #:outline? #t)
(send pd draw-text new-y-min-str (vector new-area-x-mid new-area-y-max)
'center #:outline? #t)
(send pd draw-text new-y-max-str (vector new-area-x-mid new-area-y-min)
'center #:outline? #t)
(send pd restore-drawing-params)))
(define/override (draw dc x y left top right bottom dx dy draw-caret)
;(printf "~a: drawing~n" (current-milliseconds))
(super draw dc x y left top right bottom dx dy draw-caret)
(when dragging?
(parameterize/group ([plot-parameters (get-saved-plot-parameters)])
(draw-selection dc x y (get-new-area-bounds-rect)))))
(define/override (resize w h)
(when (not (and (= w width) (= h height)))
(set! width w)
(set! height h)
(stop-message)
(when (not (update-thread-running?))
(start-update-thread #t))
(set-update #t))
#f)
))
(define (make-2d-plot-snip
init-bm saved-plot-parameters
make-bm plot-bounds-rect area-bounds-rect area-bounds->plot-bounds width height)
(make-object 2d-plot-snip%
init-bm saved-plot-parameters
make-bm plot-bounds-rect area-bounds-rect area-bounds->plot-bounds width height))