From 61e1ed676ca932f41c83a68202005a69acff7454 Mon Sep 17 00:00:00 2001 From: Jay McCarthy Date: Sat, 22 Nov 2014 10:54:08 -0800 Subject: [PATCH] Docs and contracts --- chaos.rkt | 4 +- chaos/gui.rkt | 11 +- chaos/gui/key.rkt | 2 +- chaos/pair.rkt | 8 +- scribblings/lux.scrbl | 300 +++++++++++++++++++++++++++++++++++++++++- word.rkt | 4 +- 6 files changed, 313 insertions(+), 16 deletions(-) diff --git a/chaos.rkt b/chaos.rkt index b1132ae..b68d85a 100644 --- a/chaos.rkt +++ b/chaos.rkt @@ -5,7 +5,7 @@ (define-generics chaos (chaos-yield chaos evt) (chaos-event chaos) - (chaos-output! chaos outputs) + (chaos-output! chaos output) (chaos-label! chaos label) (chaos-swap! chaos thunk) #:fallbacks @@ -13,7 +13,7 @@ (sync e)) (define (chaos-event c) never-evt) - (define (chaos-output! c os) + (define (chaos-output! c o) (void)) (define (chaos-label! c l) (void)) diff --git a/chaos/gui.rkt b/chaos/gui.rkt index 60c0720..72fcdff 100644 --- a/chaos/gui.rkt +++ b/chaos/gui.rkt @@ -13,7 +13,8 @@ (define (chaos-event c) (gui-event-ch c)) (define (chaos-output! c o) - (set-box! (gui-drawer c) o) + (when o + (set-box! (gui-drawer c) o)) ((gui-refresh! c))) (define (chaos-label! c l) (send (gui-frame c) set-label l)) @@ -60,9 +61,9 @@ (define gl-config (match mode ['draw #f] - ['compat-gl + ['gl-compat (new gl-config%)] - ['core-gl + ['gl-core (define gl-config (new gl-config%)) (send gl-config set-legacy? #f)] [gl-config @@ -98,10 +99,10 @@ [make-gui (->* () (#:mode - (or/c (one-of/c 'draw 'compat-gl 'core-gl) + (or/c (one-of/c 'draw 'gl-compat 'gl-core) (is-a?/c gl-config%)) #:icon - (or/c path-string? (is-a?/c bitmap%)) + (or/c #f path-string? (is-a?/c bitmap%)) #:width exact-nonnegative-integer? #:height diff --git a/chaos/gui/key.rkt b/chaos/gui/key.rkt index d68baa0..1071bf7 100644 --- a/chaos/gui/key.rkt +++ b/chaos/gui/key.rkt @@ -63,7 +63,7 @@ [make-key-state (-> key-state?)] [key-state-update! - (-> key-state? (is-a?/c key-event%) + (-> key-state? key-event? any)] [key-state-set? (-> key-state? (or/c char? key-code-symbol?) diff --git a/chaos/pair.rkt b/chaos/pair.rkt index 37518cc..143257a 100644 --- a/chaos/pair.rkt +++ b/chaos/pair.rkt @@ -8,7 +8,7 @@ (struct pair (l r) #:methods gen:chaos [(define/generic super-yield chaos-yield) - (define/generic super-inputs chaos-inputs) + (define/generic super-event chaos-event) (define/generic super-output! chaos-output!) (define/generic super-label! chaos-label!) (define/generic super-swap! chaos-swap!) @@ -18,10 +18,10 @@ (handle-evt always-evt (λ (_) (super-yield r e))))) - (define (chaos-inputs c) + (define (chaos-event c) (match-define (pair l r) c) - (sequence-append (super-inputs l) - (super-inputs r))) + (choice-evt (super-event l) + (super-event r))) (define (chaos-output! c o) (match-define (pair l r) c) (match-define (cons l.o r.o) o) diff --git a/scribblings/lux.scrbl b/scribblings/lux.scrbl index b36096c..f805fd6 100644 --- a/scribblings/lux.scrbl +++ b/scribblings/lux.scrbl @@ -1,10 +1,304 @@ #lang scribble/manual @require[@for-label[lux + lux/chaos + lux/chaos/gui + lux/chaos/gui/val + lux/chaos/gui/key + lux/chaos/gui/mouse + racket/gui/base racket/base]] -@title{lux} -@author{jay} +@title{lux: brilliant interactive programs} +@author{Jay McCarthy} @defmodule[lux] -Package Description Here +The @racketmodname[lux] module provides an efficient way to build +interactive programs that consist of plain mathematical functions. It +is comparable to @racketmodname[2htdp/universe], although designed to +allow more parameterization of how the program interacts. + +@local-table-of-contents[] + +@section{Structure of a @racketmodname[lux] Program} + +A @racketmodname[lux] program chooses how it will interact be +selecting a @tech{chaos} and calling @racket[call-with-chaos] with +that @tech{chaos} and a thunk that calls @racket[fiat-lux] with a +@tech{word}. @racket[fiat-lux] may be called any number of nested +times within a call to @racket[call-with-chaos]. Each subsequent +@racket[fiat-lux] takes over the @tech{chaos} until the @tech{word} +completes. It is not typically possible to use @tech{word}s with +arbitrary @tech{chaos}es, as the @tech{chaos} specifies how the +@tech{word} interacts through events and output values. + +When designing @tech{word}s, it is important to realize that +@tech{word} updating functions like @racket[word-tick] do not have to +return a @tech{word} of the same kind. + +@defproc[(call-with-chaos [c chaos?] [t (-> any)]) any]{ + +Runs @racket[t] with @racket[c] as the current @tech{chaos}.} + +@defproc[(fiat-lux [w word?]) any]{ + +Runs @racket[w] with the current @tech{chaos}.} + +@section{The Word} + +A @deftech{word} is a generic interface the encapsulates the +interactive behavior of a @racketmodname[lux] program. + +@defproc[(word? [x any/c]) boolean?]{ + +Identifies @tech{word}s.} + +@defthing[gen:word any/c]{ + +The generic interface binding for @tech{word}s.} + +The @tech{word} methods are as follows: + +@defproc[(word-fps [w word?]) flonum?]{ + +Returns the desired rate of updating for @racket[w] as +frames-per-second. By default, @racket[60.0] is returned.} + +@defproc[(word-label [w word?] [frame-time flonum?]) string?]{ + +Returns a label for @racket[w] that could use @racket[frame-time] to +show the performance of the @tech{word} rendering. By default, returns +@racket[(lux-standard-label "Lux" frame-time)].} + +@defproc[(word-event [w word?] [e any/c]) word?]{ + +Returns a @tech{word} based on @racket[w] that integrates the +information from the event @racket[e]. The type of @racket[e] is +dependent on the current @tech{chaos}. If this returns @racket[#f], +then the @racketmodname[lux] programs stops. By default, returns +@racket[w].} + +@defproc[(word-tick [w word?]) word?]{ + +Returns a @tech{word} based on @racket[w] after one tick of abstract +time. This will be called @racket[(word-fps w)] times per second. If +this returns @racket[#f], then the @racketmodname[lux] programs stops. +By default, returns @racket[w].} + +@defproc[(word-output [w word?]) any/c]{ + +Returns the output value of @racket[w]. The type that this returns is +dependent on the current @tech{chaos}. By default, returns +@racket[#f]. @tech{chaos}es should always allow @racket[#f] and use it +to mean "no output".} + +@defproc[(word-return [w word?]) any/c]{ + +Returns a value for @racket[w] when the @racketmodname[lux] programs +stops, which happens if @racket[word-event] or @racket[word-tick] +return @racket[#f].} + +@subsection{Helpers} + +@defproc[(lux-standard-label [s string?] [frame-time flonum?]) string?]{ + +Returns @racket[(string-append s ": " _fts)] where @racket[_fts] +formats @racket[frame-time] as milliseconds and as frames per +second.} + +@section{Chaos} + +A @deftech{chaos} is generic interface for an empty manifestation of +an interactive space that is given form by the @tech{word} and +@racket[fiat-lux]. + +@subsection{Racket GUI Chaos} + +@defmodule[lux/chaos/gui] + +This module provides the standard @tech{chaos} that most users of +@racketmodname[lux] will use. + +@defproc[(make-gui [#:mode mode (or/c (one-of/c 'draw 'gl-compat 'gl-core) + (is-a?/c gl-config%)) + 'draw] + [#:icon icon + (or/c #f path-string? (is-a?/c bitmap%)) + #f] + [#:width width + exact-nonnegative-integer? + 800] + [#:height height + exact-nonnegative-integer? + 600]) + chaos?]{ + +Returns a @tech{chaos} that opens a GUI frame with a canvas to draw +on. The default size of the frame is +@racket[width]x@racket[height]. The icon for the application is set to +@racket[icon]. + +The canvas is set up for drawing based on @racket[mode]. If +@racket[mode] is @racket['draw], then the canvas assumes that +@racketmodname[racket/draw] is used. If other values are used, then +the canvas is drawn with OpenGL. If @racket[mode] is +@racket['gl-compat], then a compatibility OpenGL profile is used. If +@racket[mode] is @racket['gl-core], then a core OpenGL profile is +used. If @racket[mode] is a @racket[gl-config%] object, then it is +used to initialize the canvas. + +The values that @racket[word-event] is called with are either +@racket['close] (for when the window's close button is pressed), a +@racket[key-event%] object for when keys are pressed, or a +@racket[mouse-event%] object for when the mouse is used. + +The values that @racket[word-output] should return are functions that +satisfy the contract @racket[(-> real? real? (is-a?/c dc<%>) any)] +where the first argument is the width of the canvas, the second is the +height, and the third is the canvas's drawing context.} + +@subsubsection{Drawing Values} + +@defmodule[lux/chaos/gui/val] + +This module provides a helpful function for drawing functional images +with @racketmodname[lux/chaos/gui]. + +@defproc[(make-gui-val [#:scale? scale? boolean? #t]) + (-> pict-convertible? + (-> real? real? (is-a?/c dc<%>) any))]{ + +Produces a function that draws @racket[pict-convertible?] values on to +@racketmodname[lux/chaos/gui]'s drawing context. If @racket[scale?] is +true, then the value will be scaled to file the drawing context (while +preserving aspect ratio), otherwise the value will be drawn in the +center as-is.} + +@subsubsection{Tracking Keyboard State} + +@defmodule[lux/chaos/gui/key] + +This module provides a set of functions for tracking keyboard state +for use inside of @racket[word-tick], rather than updating word state +with each event as in @racket[word-event]. Such as system may be +appropriate for interactive programs where input is only has an impact +at a consistent tick rate. + +@defstruct*[key-state ([keys hash?] [shift? boolean?] [control? boolean?] + [meta? boolean?] [alt? boolean?] [mod3? boolean?] + [mod4? boolean?] [mod5? boolean?])]{ + +Stores a mapping of which keys are presently pressed.} + +@defproc[(make-key-state) key-state?]{ + +Produces a @racket[key-state?] object with appropriate defaults.} + +@defproc[(key-event? [x any/c]) boolean?]{ + +Identifies key events.} + +@defproc[(key-state-update! [ks key-state?] [ke key-event?]) any]{ + +Updates @racket[ks] with @racket[ke].} + +@defproc[(key-state-set? [ks key-state?] [kc (or/c char? key-code-symbol?)]) boolean?]{ + +Returns true if @racket[kc] is pressed in @racket[kc].} + +@defproc[(key-state-set?! [ks key-state?] [kc (or/c char? key-code-symbol?)]) boolean?]{ + +Returns true if @racket[kc] is pressed in @racket[kc] and sets its +pressed status to false..} + +@subsubsection{Tracking Mouse State} + +@defmodule[lux/chaos/gui/mouse] + +This module provides a set of functions for tracking mouse state +for use inside of @racket[word-tick], rather than updating word state +with each event as in @racket[word-event]. Such as system may be +appropriate for interactive programs where input is only has an impact +at a consistent tick rate. + +@defstruct*[mouse-state ([x exact-integer?] + [y exact-integer?] + [left? boolean?] + [right? boolean?] + [middle? boolean?] + [shift? boolean?] + [control? boolean?] + [meta? boolean?] + [alt? boolean?] + [mod3? boolean?] + [mod4? boolean?] + [mod5? boolean?])]{ + +Stores the active state of the mouse.} + +@defproc[(make-mouse-state) mouse-state?]{ + +Produces a @racket[mouse-state?] object with appropriate defaults.} + +@defproc[(mouse-event? [x any/c]) boolean?]{ + +Identifies mouse events.} + +@defproc[(mouse-state-update! [ms mouse-state?] [me mouse-event?]) any]{ + +Updates @racket[ms] with @racket[me].} + +@subsection{Pair Chaos} + +@defmodule[lux/chaos/pair] + +This module provides a @tech{chaos} that pairs two other @tech{chaos} +objects for @racketmodname[lux] programs with multiple interfaces. + +@defproc[(make-pair [left chaos?] [right chaos?]) chaos?]{ + +Returns a @tech{chaos} where the input event type is the union of the +input events of @racket[left] and @racket[right] and the output type +is a pair of the output types of @racket[left] and @racket[right].} + +@subsection{Implementing a Chaos} + +@defmodule[lux/chaos] + +Users of @racketmodname[lux] will probably not need to implement +@tech{chaos}es, but will use those that are standard. + +@defproc[(chaos? [x any/c]) boolean?]{ + +Identifies @tech{chaos}s.} + +@defthing[gen:chaos any/c]{ + +The generic interface binding for @tech{chaos}es.} + +The @tech{chaos} methods are as follows: + +@defproc[(chaos-yield [c chaos?] [e evt?]) any]{ + +Synchronizes on @racket[e] in a way safe for @racket[c]. By default, +calls @racket[sync].} + +@defproc[(chaos-event [c chaos?]) evt?]{ + +Returns an event that when ready returns a @racket[c] event value. By +default, returns @racket[never-evt].} + +@defproc[(chaos-output! [c chaos?] [o any/c]) any]{ + +Outputs @racket[o] to @racket[c]. @tech{chaos}es should always allow +@racket[#f] and use it to mean "no output". By default, does nothing.} + +@defproc[(chaos-label! [c chaos?] [s string?]) any]{ + +Outputs @racket[s] as the label of @racket[c]. By default, does +nothing.} + +@defproc[(chaos-swap! [c chaos?] [t (-> any)]) any]{ + +Calls @racket[t] while preparing @racket[c] to run a different +@tech{word}. By default, just calls @racket[t].} diff --git a/word.rkt b/word.rkt index 13809eb..da5d662 100644 --- a/word.rkt +++ b/word.rkt @@ -21,7 +21,7 @@ (lux-standard-label "Lux" frame-time)) (define (word-event w e) w) (define (word-tick w) w) - (define (word-output w) empty) + (define (word-output w) #f) (define (word-return w) w)]) (define (lux-standard-label l frame-time) @@ -98,6 +98,8 @@ (provide gen:word (contract-out + [word? + (-> any/c word?)] [lux-standard-label (-> string? flonum? string?)]