lux/scribblings/lux.scrbl

387 lines
14 KiB
Plaintext
Raw Normal View History

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.
2016-04-08 10:19:29 +00:00
Check out some examples in the
@link["https://github.com/jeapostrophe/lux/tree/master/examples"]{examples}
directory in the source.
2014-11-22 18:54:08 +00:00
@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)].}
2015-04-14 18:00:59 +00:00
@defproc[(word-evt [w word?]) evt?]{
Returns a synchronizable event that the @tech{word} @racket[w]
requires notification of. By default, returns @racket[never-evt].}
2014-11-22 18:54:08 +00:00
@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]
2018-12-12 18:45:39 +00:00
return @racket[#f]. By default, returns @racket[w].}
@subsection{Word Construction}
A @tech{word} can be created by defining a new @racket[struct] that
implements the @racket[gen:word] generic interface@";" or, it can be
defined using @racket[word]. The first method is best when it is easy
to capture the state of the creation in a structure and the second is
preferable when it is better to capture the state implicitly in the
captured closures. In the author's experience, the second is also best
for creations with complex control flow, because different sorts of
@tech{word}s can be returned in different circumstances.
@defproc[(word [base (or/c #f word?) #f]
2019-07-12 23:28:08 +00:00
[#:fps fps real? ....]
[#:label label (or/c string? (-> real? string?)) ....]
[#:evt evt evt? ....]
[#:event event (-> any/c (or/c #f word?)) ....]
[#:tick tick (-> (or/c #f word?)) ....]
[#:output output any/c ....]
[#:return return any/c ....])
2018-12-12 18:45:39 +00:00
word?]{
Return a @tech{word} where the implementations of the methods are as
given or inherited from @racket[base] or the defaults (described
above). The only subtleties are that: (1) @racket[label] may be a
string which is used directly and the frame time is not
available@";" (2) @racket[event] is a function that only receives the
generated event@";" (3) @racket[tick] is a thunk. The assumption is
that the caller of @racket[word] can arrange for the value returned to
be captured by these closures if necessary.
}
2014-11-22 18:54:08 +00:00
2019-07-12 23:28:08 +00:00
@defform[(word/rec x:id word-args ...)]{Expands to @racket[(letrec ([x (word word-args ...)]) x)].}
2014-11-22 18:54:08 +00:00
@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%))
2015-05-03 12:11:46 +00:00
'draw]
[#:opengl-hires? opengl-hires? boolean? #f]
2014-11-26 22:21:55 +00:00
[#:start-fullscreen? start-fullscreen? boolean?
2015-05-03 12:11:46 +00:00
#f]
[#:frame-style frame-style (listof symbol?) '()]
2014-11-22 18:54:08 +00:00
[#:icon icon
(or/c #f path-string? (is-a?/c bitmap%))
#f]
[#:x x
(or/c exact-nonnegative-integer? (one-of/c 'left 'center 'right))
'center]
[#:y y
(or/c exact-nonnegative-integer? (one-of/c 'top 'center 'bottom))
'center]
2014-11-22 18:54:08 +00:00
[#:width width
exact-nonnegative-integer?
800]
[#:height height
exact-nonnegative-integer?
600]
[#:monitor monitor
(or/c false/c exact-nonnegative-integer?)
#f])
2014-11-22 18:54:08 +00:00
chaos?]{
Returns a @tech{chaos} that opens a GUI frame with a canvas to draw on.
The frame is placed at position @racket[x],@racket[y] on monitor @racket[monitor];
if @racket[monitor] is @racket[#f], the monitor containing the mouse pointer is used.
The default size of the frame is
2014-11-22 18:54:08 +00:00
@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
2015-05-03 12:11:46 +00:00
is initially fullscreen. The frame's style is set to
@racket[frame-style].
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
2015-05-03 12:11:46 +00:00
used to initialize the canvas. If @racket[opengl-hires?] is
@racket[#t], then the resulting @racket[gl-config%] object will have
high resolution mode set.
2014-11-22 18:54:08 +00:00
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])
2014-11-22 18:54:08 +00:00
(-> 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.}
2014-11-30 21:47:42 +00:00
@defproc[(key-event-code [ke key-event?]) (or/c (cons/c 'release (or/c char? key-code-symbol?)) (or/c char? key-code-symbol?))]{
Returns the code of the key event.}
2014-11-22 18:54:08 +00:00
@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.
2014-11-30 21:47:42 +00:00
@defstruct*[mouse-state ([x real?]
[y real?]
2014-11-22 18:54:08 +00:00
[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.}
2014-11-30 21:47:42 +00:00
@defproc[(mouse-event-xy [me mouse-event?]) (values real? real?)]{
Returns the position of the mouse event.}
2014-11-22 18:54:08 +00:00
@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.}