diff --git a/prospect/big-bang.rkt b/prospect/big-bang.rkt new file mode 100644 index 0000000..5d8e340 --- /dev/null +++ b/prospect/big-bang.rkt @@ -0,0 +1,177 @@ +#lang racket/base + +(provide big-bang-world + big-bang-universe + (struct-out window) + (struct-out to-server) + (struct-out from-server) + (struct-out tick-event) + (struct-out key-event) + (struct-out pad-event) + (struct-out release-event) + (struct-out mouse-event) + (struct-out mouse-state) + (struct-out active-window) + update-window + (all-from-out 2htdp/image)) + +(require racket/set) +(require racket/match) +(require 2htdp/image) +(require (except-in 2htdp/universe + key-event? + pad-event? + mouse-event?)) +(require (only-in 2htdp/private/check-aux SQPORT)) +(require (only-in racket/list flatten)) + +(require "main.rkt") +(require "route.rkt") + +;;--------------------------------------------------------------------------- + +(struct window (id x y z image) #:transparent) + +(struct to-server (message) #:transparent) +(struct from-server (message) #:transparent) +(struct tick-event () #:transparent) +(struct key-event (key window) #:transparent) +(struct pad-event (key window) #:transparent) +(struct release-event (key window) #:transparent) +(struct mouse-event (x y window type) #:transparent) +(struct mouse-state (x y window) #:transparent) +(struct active-window (id) #:transparent) + +(define (update-window id x y image #:z [z 0]) + (patch-seq (retract (window id ? ? ? ?) #:meta-level 1) + (assert (window id x y z image) #:meta-level 1))) + +;;--------------------------------------------------------------------------- + +(struct bb (world windows inbound outbound halted? x y) #:transparent) + +(define window-projection (compile-projection (?! (window ? ? ? ? ?)))) + +(define (inject b es) + (interpret-actions (struct-copy bb b [inbound (append (bb-inbound b) + es + (list (update-active-window + (find-active-window b))))]) + #f + #t)) + +(define (incorporate-patch b p) + (define-values (added removed) (patch-project/set/single p window-projection)) + (struct-copy bb b + [windows (sort (set->list (set-union added + (set-subtract (list->set (bb-windows b)) + removed))) + (lambda (w1 w2) (< (window-z w1) (window-z w2))))] + [halted? (or (and (bb-halted? b) + (not (matcher-match-value (patch-removed p) 'stop #f))) + (matcher-match-value (patch-added p) 'stop #f))])) + +(define (deliver b e) + (clean-transition (world-handle-event e (bb-world b)))) + +(define (interpret-actions b txn need-poll?) + (match txn + [#f ;; inert + (match (bb-inbound b) + ['() + (if need-poll? + (interpret-actions b (deliver b #f) #f) + (let ((outbound (reverse (bb-outbound b)))) + (if (null? outbound) + b + (make-package (struct-copy bb b [outbound '()]) outbound))))] + [(cons e rest) + (let ((b (struct-copy bb b [inbound rest]))) + (interpret-actions b (deliver b e) #t))])] + [(transition new-world actions) + (let process-actions ((b (struct-copy bb b [world new-world])) (actions actions)) + (match actions + ['() (interpret-actions b #f #t)] + [(cons a actions) + (process-actions (match a + [(? patch? p) + (incorporate-patch b p)] + [(message (to-server sexp)) + (struct-copy bb b + [outbound (cons sexp (bb-outbound b))])] + [_ b]) + actions)]))])) + +(define (inside? mx my x y image) + (and (>= mx x) + (>= my y) + (< (- mx x) (image-width image)) + (< (- my y) (image-height image)))) + +(define (find-active-window b) + (define mx (bb-x b)) + (define my (bb-y b)) + (let loop ((ws (bb-windows b))) + (match ws + ['() #f] + [(cons (window id x y _ image) ws) + (if (inside? mx my x y image) id (loop ws))]))) + +(define (render b) + (for/fold [(scene empty-image)] [(w (bb-windows b))] + (match-define (window _ x y z image) w) + (overlay/xy scene x y image))) + +(define (update-active-window active-id) + (patch-seq (retract (active-window ?)) + (assert (active-window active-id)))) + +(define-syntax-rule (big-bang-world* boot-actions extra-clause ...) + (big-bang (interpret-actions (bb (make-world boot-actions) + '() + '() + '() + #f + 0 + 0) + #f + #t) + (on-tick (lambda (b) + (inject b (list (message (tick-event)))))) + (on-key (lambda (b k) + (inject b (list (message (key-event k (find-active-window b))))))) + ;; (on-pad (lambda (b p) + ;; (inject b (list (message (pad-event p (find-active-window b))))))) + (on-release (lambda (b k) + (inject b (list (message (release-event k (find-active-window b))))))) + (on-mouse (lambda (b0 x y e) + (define b (struct-copy bb b0 [x x] [y y])) + (define active-id (find-active-window b)) + (inject b (list (patch-seq (retract (mouse-state ? ? ?)) + (assert (mouse-state x y active-id))) + (message (mouse-event x y active-id e)))))) + (stop-when bb-halted?) + extra-clause ...)) + +(define-syntax-rule (big-bang-world** width height boot-actions extra-clause ...) + (if (and width height) + (big-bang-world* boot-actions (to-draw render width height) extra-clause ...) + (big-bang-world* boot-actions (to-draw render) extra-clause ...))) + +(define (big-bang-world #:width [width #f] + #:height [height #f] + . boot-actions) + (big-bang-world** width height boot-actions)) + +(define (big-bang-universe #:width [width #f] + #:height [height #f] + #:register [ip LOCALHOST] + #:port [port-number SQPORT] + #:name [world-name (gensym 'prospect)] + . boot-actions) + (big-bang-world** width height boot-actions + (on-receive (lambda (b sexps) + (inject b (for/list ((m sexps)) (message (from-server m)))))) + (register ip) + (port port-number) + (name world-name))) diff --git a/prospect/examples/big-bang.rkt b/prospect/examples/big-bang.rkt new file mode 100644 index 0000000..274baa3 --- /dev/null +++ b/prospect/examples/big-bang.rkt @@ -0,0 +1,71 @@ +#lang racket + +(require "../main.rkt") +(require "../big-bang.rkt") + +(define (button #:background [background "grey"] + #:foreground [foreground "white"] + #:font-size [font-size 22] + name x y label callback) + (spawn (lambda (e s) + (match e + [(message (at-meta (mouse-event _ _ _ "button-down"))) (transition s (callback))] + [_ #f])) + (void) + (let ((label-image (text label font-size foreground))) + (update-window name x y + (overlay label-image + (rectangle (+ (image-width label-image) 20) + (+ (image-height label-image) 20) + "solid" + background)))) + (sub (mouse-event ? ? name ?) #:meta-level 1))) + +(define (draggable-shape name orig-x orig-y image) + (struct idle (ticks x y) #:transparent) + (struct dragging (dx dy) #:transparent) + (define (move-to x y) (update-window name x y image #:z 10)) + (define (mouse-sub active-pat) + (patch-seq (unsub (mouse-event ? ? ? ?) #:meta-level 1) + (sub (mouse-event ? ? active-pat ?) #:meta-level 1))) + (spawn (match-lambda** + [((message (at-meta (tick-event))) (idle ticks bx by)) + (define new-ticks (+ ticks 1)) + (define displacement (* (cos (* new-ticks 10 1/180 pi)) 4)) + (define new-y (+ by displacement)) + (transition (idle new-ticks bx new-y) + (move-to bx new-y))] + [((message (at-meta (mouse-event mx my _ "button-down"))) (idle _ bx by)) + (transition (dragging (- mx bx) (- my by)) (mouse-sub ?))] + [((message (at-meta (mouse-event mx my _ "drag"))) (dragging dx dy)) + (transition (dragging dx dy) (move-to (- mx dx) (- my dy)))] + [((message (at-meta (mouse-event mx my _ (or "leave" "button-up")))) (dragging dx dy)) + (transition (idle 0 (- mx dx) (- my dy)) + (list (move-to (- mx dx) (- my dy)) + (mouse-sub name)))] + [(_ _) #f]) + (idle 0 orig-x orig-y) + (sub (tick-event) #:meta-level 1) + (mouse-sub name) + (move-to orig-x orig-y))) + +(big-bang-world #:width 640 + #:height 480 + (spawn (lambda (e s) + (match e + [(? patch? p) + (define-values (in out) + (patch-project/set/single p + (compile-projection + (at-meta (?! (active-window ?)))))) + (log-info "~v" in) + #f] + [_ #f])) + (void) + (sub (active-window ?) #:meta-level 1)) + (button #:background "red" 'stop-button 0 0 "Exit" + (lambda () (assert 'stop #:meta-level 1))) + (draggable-shape 'c1 50 50 (circle 30 "solid" "orange")) + (draggable-shape 's1 100 100 (star 40 "solid" "firebrick"))) + +(exit 0)