[racket] Making animations in racket (or, why racket is hard to transition to from scratch)

From: Stephen Bloch (sbloch at adelphi.edu)
Date: Wed Feb 2 07:20:13 EST 2011

On Feb 1, 2011, at 9:27 PM, Yaron Minsky wrote:

> I am trying to teach my scratch-addicted son a bit about racket, and I'm finding it to be rather tough going, mostly because a lot of things that are really easy in scratch seem surprisingly hard in racket.  Also, even when I can get things done, the performance of the graphics leaves something to be desired.  I'm wondering if this is just the way the world is right now, or if somehow I'm approaching this the wrong way.
> I was trying to put together a simple animation using the universe and image teachpacks where:
> - there is a "sprite" which switches between two images periodically
> - the sprite is always pointing at the mouse

When I run this code, the sprite points at the mouse as long as the mouse is to the right of the sprite; once I move to its left, the sprite rotates in the wrong direction.

And there are some minor inefficiencies that have nothing to do with the graphics-and-animation library.

As it happens, one can address both of these at the same time: replace
       (hyp (posn-mag delta))
       (angle-in-radians (if (= hyp 0) 0 (asin (/ (posn-y delta) hyp))))
       (angle-in-radians (if (= (posn-x delta) (posn-y delta) 0)
                             (atan (posn-y delta) (posn-x delta))))
which requires not only less computation (you can drop the "hyp" function entirely) but less knowledge of trig.

There are a number of other things that would simplify the code, some of which would also make things (slightly) more efficient.

Your sprite switches between its two images every 10 clock-ticks, where a clock-tick defaults to 1/28 second.  Instead, just set the tick interval to what you want, and skip the 0.1 and the rounding.

(big-bang ...
      (on-tick tick (/ 1 2.8))   ; or (on-tick tick 1/3) or (on-tick tick .382) or whatever
      ... )

And since there are only two possible images in the world, I would probably use a boolean or a pair of symbols or strings to choose between them:

(define WIDTH 400)
(define HEIGHT 400)
(define BACKGROUND (empty-scene WIDTH HEIGHT))
(define CENTER (make-posn (/ WIDTH 2) (/ HEIGHT 2)))
(define-struct world (pos mouse-pos sprite))
(define initial-world (make-world CENTER CENTER "fred"))
(define (next-sprite current)
   (cond [(string=? current "fred") "george"]
              [(string=? current "george") "fred"]))
(define (sprite-image current)
   (cond [(string=? current "fred") pic1]
              [(string=? current "george") pic2]))
(define (draw w)
      (rotate (angle-between (world-pos w) (world-mouse-pos w))
                  (sprite-image (world-sprite w)))
      (posn-x (world-pos w))
      (posn-y (world-pos w))

(Symbols or booleans would be slightly more efficient, but I introduce strings much earlier in my course than symbols or booleans.)

> Another thing I'd be interested in suggestions with is how to deal with updating a struct in a clean way.  Right now, you need to explicitly wrote set-world-X functions for each field in your struct, which is pretty ugly.  Also, the fact that structs don't have field names makes them more error prone.  None of this is well suited towards building complex worlds with lots of components.

I'm not sure what you mean by "structs don't have field names", but the issue of updating individual fields of a struct is under discussion; in a version Fairly Soon the student languages will probably have functions that produce a copy of a given struct with a single field replaced.

Stephen Bloch
sbloch at adelphi.edu

Posted on the users mailing list.