#lang scheme/gui
(require 2htdp/private/check-aux
2htdp/private/timer
2htdp/private/last
2htdp/private/checked-cell
(prefix-in 2p: 2htdp/private/image)
htdp/error
htdp/image
mzlib/runtime-path
mrlib/bitmap-label
string-constants
mrlib/gif)
(provide (all-from-out htdp/image))
(provide (rename-out [my-place-image place-image]
[my-scene+line scene+line]
[2p:nw:rectangle nw:rectangle] [2p:empty-scene empty-scene] )
scenify)
(define (scenify img)
(put-pinhole img 0 0))
(define (my-place-image fg x y bg)
(2p:place-image fg x y (scenify bg)))
(define (my-scene+line bg x0 y0 x1 y1 color)
(2p:scene+line (scenify bg) x0 y0 x1 y1 color))
(define (show-it img)
(check-arg 'show-it (image? img) 'image "first" img)
img)
(define (my-check-scene-result tname thing)
(if (image? thing)
(scenify thing)
(check-result tname #f "image" thing)))
(provide world% aworld% show-it)
(define-struct package (world message) #:transparent)
(define (create-package w m)
(check-arg 'make-package (sexp? m) 'sexp "second" m)
(make-package w m))
(provide
(rename-out (create-package make-package)) package? )
(define world%
(last-mixin
(clock-mixin
(class* object% (start-stop<%>)
(inspect #f)
(init-field
world0 (name #f) (state #f) (register #f) (check-with True) (tick K))
(init
(on-key K) (on-mouse K) (on-receive #f) (on-draw show-it) (stop-when False) (record? #f))
(field
[world
(new checked-cell% [msg "World"] [value0 world0] [ok? check-with]
[display (and state (or name "your world program's state"))])])
(field [*out* #f] [*rec* (make-custodian)])
(define/private (register-with-host)
(define FMT "\nworking off-line\n")
(define FMTtry
(string-append "unable to register with ~a after ~s tries"
FMT))
(define FMTcom
(string-append "unable to register with ~a due to protocol problems"
FMT))
(define (RECEIVE in)
(define (RECEIVE)
(sync
(handle-evt
in
(lambda (in)
(define dis (text "the universe disappeared" 11 'red))
(with-handlers ((tcp-eof?
(compose (handler #f)
(lambda (e)
(set! draw (lambda (w) dis))
(pdraw)
e))))
(define msg (tcp-receive in))
(cond
[(sexp? msg) (prec msg) (RECEIVE)] [#t (error 'RECEIVE "sexp expected, received: ~e" msg)]))))))
RECEIVE)
(parameterize ([current-custodian *rec*])
(let try ([n TRIES])
(printf "trying to register with ~a ...\n" register)
(with-handlers ((tcp-eof? (lambda (x) (printf FMTcom register)))
(exn:fail:network?
(lambda (x)
(if (= n 1)
(printf FMTtry register TRIES)
(begin (sleep PAUSE) (try (- n 1)))))))
(define-values (in out) (tcp-connect register SQPORT))
(tcp-register in out name)
(printf "... successful registered and ready to receive\n")
(set! *out* out)
(thread (RECEIVE in))))))
(define/private (broadcast msg)
(when *out*
(check-result 'send sexp? "Sexp expected; given ~e\n" msg)
(tcp-send *out* msg)))
(field
(draw (cond
[(procedure? on-draw) on-draw]
[(pair? on-draw) (first on-draw)]
[else on-draw]))
(live (not (boolean? draw)))
(width (if (pair? on-draw) (second on-draw) #f))
(height (if (pair? on-draw) (third on-draw) #f)))
(field [enable-images-button void] [disable-images-button void]
[visible (new pasteboard%)])
(define (show-canvas)
(send visible set-cursor (make-object cursor% 'arrow))
(let ([fst-scene (ppdraw)])
(set! width (if width width (image-width fst-scene)))
(set! height (if height height (image-height fst-scene)))
(create-frame)
(show fst-scene)))
(define/pubment (create-frame)
(define play-back:cust (make-custodian))
(define frame (new (class frame%
(super-new)
(define/augment (on-close)
(callback-stop! 'frame-stop)
(custodian-shutdown-all play-back:cust)))
(label (if name (format "~a" name) "World"))
(stretchable-width #f)
(stretchable-height #f)
(style '(no-resize-border metal))))
(define editor-canvas
(new (class editor-canvas%
(super-new)
(define/override (on-char e)
(when live (pkey (key-event->parts e))))
(define/override (on-event e)
(define-values (x y me) (mouse-event->parts e))
(when live
(cond
[(and (<= 0 x width) (<= 0 y height)) (pmouse x y me)]
[(memq me '(leave enter)) (pmouse x y me)]
[else (void)]))))
(parent frame)
(editor visible)
(style '(no-hscroll no-vscroll))
(horizontal-inset INSET)
(vertical-inset INSET)))
(send editor-canvas min-client-width (+ width INSET INSET))
(send editor-canvas min-client-height (+ height INSET INSET))
(set!-values (enable-images-button disable-images-button)
(inner (values void void) create-frame frame play-back:cust))
(send editor-canvas focus)
(send frame show #t))
(define/public (show pict)
(send visible begin-edit-sequence)
(send visible lock #f)
(let ([s (send visible find-first-snip)]
[c (send visible get-canvas)])
(when s (send visible delete s))
(send visible insert (send pict copy) 0 0))
(send visible lock #t)
(send visible end-edit-sequence))
(field
(key on-key)
(mouse on-mouse)
(rec on-receive))
(define-syntax-rule (def/pub-cback (name arg ...) transform)
(define/public (name arg ...)
(queue-callback
(lambda ()
(with-handlers ([exn? (handler #t)])
(define tag (format "~a callback" 'transform))
(define nw (transform (send world get) arg ...))
(when (package? nw)
(broadcast (package-message nw))
(set! nw (package-world nw)))
(let ([changed-world? (send world set tag nw)])
(unless changed-world?
(when draw (pdraw))
(when (pstop)
(when last-picture
(set! draw last-picture)
(pdraw))
(callback-stop! 'name)
(enable-images-button)))
changed-world?))))))
(def/pub-cback (ptock) tick)
(def/pub-cback (pkey ke) key)
(def/pub-cback (pmouse x y me) mouse)
(def/pub-cback (prec msg) rec)
(define/private (pdraw) (show (ppdraw)))
(define/private (ppdraw)
(my-check-scene-result (name-of draw 'your-draw) (draw (send world get))))
(field [stop (if (procedure? stop-when) stop-when (first stop-when))]
[last-picture (if (pair? stop-when) (second stop-when) #f)])
(define/private (pstop)
(define result (stop (send world get)))
(check-result (name-of stop 'your-stop-when) boolean? "boolean" result)
result)
(define/public (callback-stop! msg)
(stop! (send world get)))
(define (handler re-raise)
(lambda (e)
(printf "breaking ..\n")
(disable-images-button)
(stop! (if re-raise e (send world get)))))
(define/public (start!)
(queue-callback
(lambda ()
(when draw (show-canvas))
(when register (register-with-host)))))
(define/public (stop! w)
(set! live #f)
(custodian-shutdown-all *rec*))
(super-new)
(start!)
(when (stop (send world get)) (stop! (send world get)))))))
(define-runtime-path break-btn:path '(lib "icons/break.png"))
(define break-button:label
((bitmap-label-maker (string-constant break-button-label) break-btn:path) '_))
(define-runtime-path image-button:path '(lib "icons/file.gif"))
(define image-button:label ((bitmap-label-maker "Images" image-button:path) '_))
(define aworld%
(class world% (super-new)
(inherit-field world0 tick key mouse rec draw rate width height)
(inherit show callback-stop!)
(define/augment (create-frame frm play-back-custodian)
(define p (new horizontal-pane% [parent frm][alignment '(center center)]))
(define (switch)
(send stop-button enable #f)
(send image-button enable #t))
(define (stop)
(send image-button enable #f)
(send stop-button enable #f))
(define-syntax-rule (btn l a y ...)
(new button% [parent p] [label l] [style '(border)]
[callback (lambda a y ...)]))
(define stop-button
(btn break-button:label (b e) (callback-stop! 'stop-images) (switch)))
(define image-button
(btn image-button:label (b e)
(parameterize ([current-custodian play-back-custodian])
(thread (lambda () (play-back)))
(stop))))
(send image-button enable #f)
(values switch stop))
(field [event-history '()]) (define/private (add-event type . stuff)
(set! event-history (cons (cons type stuff) event-history)))
(define-syntax-rule (def/over-cb (pname name arg ...))
(define/override (pname arg ...)
(when (super pname arg ...) (add-event name arg ...))))
(def/over-cb (ptock tick))
(def/over-cb (pkey key e))
(def/over-cb (pmouse mouse x y me))
(def/over-cb (prec rec m))
(define/private (play-back)
(define (world-transition world fst) (apply (car fst) world (cdr fst)))
(define total (+ (length event-history) 1))
(define digt# (string-length (number->string total)))
(define imag# 0)
(define bmps '())
(define (save-image img)
(define bm (make-object bitmap% width height))
(define dc (make-object bitmap-dc% bm))
(send dc clear)
(send img draw dc 0 0 0 0 width height 0 0 #f)
(set! imag# (+ imag# 1))
(send bm save-file (format "i~a.png" (zero-fill imag# digt#)) 'png)
(set! bmps (cons bm bmps)))
(define img:dir (get-directory "image directory:" #f (current-directory)))
(when img:dir
(parameterize ([current-directory img:dir])
(define last
(foldr (lambda (event world)
(save-image (draw world))
(show (text (format "~a/~a created" imag# total) 18 'red))
(world-transition world event))
world0
event-history))
(show (text (format "creating ~a" ANIMATED-GIF-FILE) 18 'red))
(create-animated-gif rate (reverse bmps))
(show (draw last)))))))
(define (create-animated-gif R bitmap-list)
(when (file-exists? ANIMATED-GIF-FILE) (delete-file ANIMATED-GIF-FILE))
(write-animated-gif bitmap-list (if (> +inf.0 R 0) (number->integer R) 5)
ANIMATED-GIF-FILE
#:one-at-a-time? #t
#:loop? #f))
(define ANIMATED-GIF-FILE "i-animated.gif")