2014-11-19 15:17:36 +00:00
|
|
|
#lang scribble/manual
|
|
|
|
@require[@for-label[lux
|
2014-11-22 18:54:08 +00:00
|
|
|
lux/chaos
|
|
|
|
lux/chaos/gui
|
|
|
|
lux/chaos/gui/val
|
|
|
|
lux/chaos/gui/key
|
|
|
|
lux/chaos/gui/mouse
|
2014-11-23 19:20:22 +00:00
|
|
|
racket/contract
|
|
|
|
pict/convert
|
2014-11-22 18:54:08 +00:00
|
|
|
racket/gui/base
|
2014-11-19 15:17:36 +00:00
|
|
|
racket/base]]
|
|
|
|
|
2014-11-22 18:54:08 +00:00
|
|
|
@title{lux: brilliant interactive programs}
|
|
|
|
@author{Jay McCarthy}
|
2014-11-19 15:17:36 +00:00
|
|
|
|
|
|
|
@defmodule[lux]
|
|
|
|
|
2014-11-22 18:54:08 +00:00
|
|
|
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]
|
2014-11-26 22:21:55 +00:00
|
|
|
[#:start-fullscreen? start-fullscreen? boolean?
|
|
|
|
#f]
|
2014-11-22 18:54:08 +00:00
|
|
|
[#: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
|
2014-11-26 22:21:55 +00:00
|
|
|
@racket[icon]. If @racket[start-fullscreen?] is true, then the frame
|
|
|
|
is initially fullscreen.
|
2014-11-22 18:54:08 +00:00
|
|
|
|
|
|
|
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:
|
|
|
|
|
2014-11-26 22:21:55 +00:00
|
|
|
@defproc[(chaos-start! [c chaos?]) any]{
|
|
|
|
|
|
|
|
Called at the start of using @racket[c] as the current
|
|
|
|
@tech{chaos}. By default, does nothing.}
|
|
|
|
|
2014-11-22 18:54:08 +00:00
|
|
|
@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].}
|
2014-11-26 22:21:55 +00:00
|
|
|
|
|
|
|
@defproc[(chaos-stop! [c chaos?]) any]{
|
|
|
|
|
|
|
|
Called at the end of using @racket[c] as the current @tech{chaos}. By
|
|
|
|
default, does nothing.}
|