#lang scribble/doc @(require scribble/manual (for-label scheme "sb-world.ss" teachpack/htdp/image)) @title{SB-world Teachpack} @defmodule[sb-world/sb-world] @section{About this teachpack} The @bold{sb-world} teachpack resembles the built-in @bold{world} teachpack, but is used in a more functional, less imperative way. To wit, rather than calling @bold{big-bang} to start an animation, and @italic{then} calling @bold{on-tick}, @bold{on-mouse-event}, etc. to install its handlers, you'll call @bold{run-animation} with the handlers as arguments. @section{Installation} If you're reading this, you've probably already installed the teachpack successfully, but if you need to install it on a different machine, ... @itemize{ @item{start DrScheme} @item{switch languages to Module and click "Run"} @item{in the Interactions pane, type @schemeblock[(require (planet "install.ss" ("sbloch" "sb-world.plt" 1 2)))]} @item{after a few seconds, you should see the message "Wrote file "sb-world.ss" to installed-teachpacks directory."} @item{switch languages back to one of the HtDP languages, like Beginning Student} @item{from the Language menu, choose "Add Teachpack..." and select "sb-world.ss"} @item{click "Run"} } @section{Data types and functions} @deftech{model} @scheme[any/c] When you write an animation, one of your first decisions is usually "what type is the model?" If you don't choose a data definition for @tech{model}, it will default to "image", but this is rather limiting. @deftech{key-event} @scheme[or/c char? symbol?] Key presses are represented as either characters (for ordinary keys: letters, digits, punctuation) or as symbols (for function keys, arrow keys, delete, page-up, page-down, etc.) In addition, when a key is released, you get a @scheme['release] event, also a symbol. The teachpack provides two useful functions for this data type: @scheme[key-event?], which tells whether something is a @tech{key-event}, and @scheme[key=?], which compares two key events for equality. @defproc[(run-animation (width number?) (height number?) (initial-model (unsyntax @tech{model})) (tick-interval number?) (handler1 handler?) ...) true]{ The most important function in the teachpack. You give it the width and height that you want for the animation window, an initial model, how many seconds between clock ticks, and possibly some "handlers" (see below), and it will run an animation for you. For details, see Chapter 7 of my textbook. } @defproc[(run-saveable-animation (width number?) (height number?) (initial-model (unsyntax @tech{model})) (tick-interval number?) (handler1 handler?) ...) true]{ Just like @scheme{run-animation}, except that it records the sequence of events and, after it's finished, allows you to save it as an animated GIF picture. } @defproc[(place-image (foreground image?) (x number?) (y number?) (background image?)) image?]{ Places the foreground image at the specified location on the background image, treating (0,0) as the top-left corner of the background. @bold{Note:} Unlike the version of @scheme{place-image} in the standard @bold{world} teachpack, this version accepts @italic{any} image as the background, regardless of pinholes. } @defproc[(empty-scene (width number?) (height number?)) image?]{ An easy way to create a white box, outlined in black, of the specified width and height, generally to use as a background image. } @defproc[(end-of-time (result any/c)) any/c]{ Stops the animation from which it was called, and returns the specified value (usually a string, but could be an image, a number, etc.) @bold{Note:} This function should only be called from within a tick, mouse, or key handler. If you call it from @scheme{run-animation}, you've basically stopped the animation before it can start. } @defproc[(key-event? (event any/c)) boolean?]{ Tells whether something is a @tech{key-event}.} @defproc[(key=? (event1 key-event?) (event2 key-event?)) boolean?]{ Compares two @tech{key-event}s for equality; won't crash even if one happens to be a character and the other a symbol.} @section{Handlers} @deftech{handler} When you run an animation, you can provide handler functions to tell the animation how to do various things: how to redraw the screen from the model, how to change the model at every clock tick, how to change the model when the user uses the mouse or keyboard, etc. Once you've written a function to be used as a handler, you specify what kind of handler it is by calling @scheme{on-redraw}, @scheme{on-tick}, @scheme{on-mouse}, @scheme{on-key}, or @scheme{stop-when}, and passing the result to run-animation. @defproc[(on-redraw (redraw-handler (-> (unsyntax @tech{model}) image))) handler?]{ To specify how the animation is to be shown on the screen (and in particular if your @tech{model} is anything other than an image), you need to write a function from @tech{model} to image, and supply it as an argument to @scheme{on-redraw}. This function will be called on the current model every time the model changes, and the resulting image will appear on the screen. @bold{Note:} If you don't provide a redraw handler, the system will assume that your @tech{model} is simply an image, and will show that image itself in the animation window. } @defproc[(on-tick (tick-handler (-> (unsyntax @tech{model}) (unsyntax @tech{model})))) handler?]{ If you want your animation to do something at regular time intervals, you need to write a function from @tech{model} to @tech{model}, and supply it as an argument to @scheme{on-tick}. Every time the clock ticks, this function will be called on the current model, and its result will become the new model. } @defproc[(on-mouse (mouse-handler (-> (unsyntax @tech{model}) (x number?) (y number?) (event symbol?) (unsyntax @tech{model})))) handler?]{ If you want your animation to respond to the mouse, you need to write a function that takes in a @tech{model}, two numbers, and a symbol, and returns a @tech{model}, and provide this function as an argument to @scheme{on-mouse}. Every time the mouse is moved or clicked, your function will be called on the current @tech{model}, the coordinates of the mouse, and what was done to the mouse, and its result will be the new @tech{model}. Possible events are @schemeidfont{'button-down}, @schemeidfont{'button-up}, @schemeidfont{'move}, @schemeidfont{'drag}, @schemeidfont{'wheel-up}, @schemeidfont{'wheel-down}, @schemeidfont{'enter}, and @schemeidfont{'leave}. } @defproc[(on-key (key-handler (-> (unsyntax @tech{model}) (key (or/c char? symbol?)) (unsyntax @tech{model})))) handler?]{ If you want your animation to respond to the keyboard, you need to write a function that takes in a @tech{model} and a @tech{key-event} and returns a @tech{model}, and provide this function as an argument to @scheme{on-key}. Every time a key is pressed or released on the keyboard, your function will be called with the current model and the relevant key (or the symbol @scheme{'release}), and its result will be used as the new model. Possible keys, in addition to ordinary characters, are @schemeidfont{'release}, @schemeidfont{'up}, @schemeidfont{'down}, @schemeidfont{'left}, @schemeidfont{'right}, @schemeidfont{'next}, @schemeidfont{'prior}, @schemeidfont{'home}, @schemeidfont{'numpad-enter}, @schemeidfont{'separator}, @schemeidfont{'f1}, @schemeidfont{'f2}, ... } @defproc[(stop-when (quit-tester (-> (unsyntax @tech{model}) boolean?))) handler?]{ If you want your animation to stop when a particular thing happens, you have two choices. One is to use @scheme{end-of-time}, as discussed above. The other is to write a function from @tech{model} to boolean, and provide this function as an argument to @scheme{stop-when}. This function will be called on the current @tech{model} every time the @tech{model} changes; if it returns true, the animation will end. @bold{Note:} There should be no problem with using both features at once, i.e. installing a @scheme{stop-when} handler and also having some other handler call @scheme{end-of-time}. }