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

From: Yaron Minsky (yminsky at gmail.com)
Date: Tue Feb 1 21:27:27 EST 2011

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

I wrote some code for doing this.  One problem is that getting this to work
required some actual trigonometry, which my 9 year old isn't quite ready
for.  But that I could fix by writing a simple helper function.  But the
other problem is that the resulting code is horribly slow.  As soon as you
rotate the image, the rendering performance falls through the floor, and you
see a ton of flickering.

I've attached the code below.  I can imagine some ways of optimizing this
(for example, doing the rotations only on mouse moves, and storing the
resulting rotated images in the world-struct), but I want to keep the coding
style pretty simple, and I think these kinds of optimizations get in the way
of comprehensibility.

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 wondering if there is a better way of approaching
this that doesn't require quadratic boilerplate, and that is suitable for
teaching to a kid.

Anyway, here's the code.  Thanks in advance!

;; the images here are actual PNG images, which don't show up nicely in
ascii...
(define pic1 .)
(define pic2 .)

(define (posn-diff a b)
  (make-posn
   (- (posn-x a) (posn-x b))
   (- (posn-y a) (posn-y b))))

(define (posn-mag p)
  (sqrt (+ (sqr (posn-x p)) (sqr (posn-y p)))))

(define (angle-between pos1 pos2)
  (let*
      ((delta (posn-diff pos2 pos1))
       (hyp (posn-mag delta))
       (angle-in-radians (if (= hyp 0) 0 (asin (/ (posn-y delta) hyp))))
       )
    (-(* angle-in-radians (/ 180 pi)))))

(define-struct world (pos mouse-pos time))
(define initial-world (make-world (make-posn 200 200) (make-posn 200 200)
0))

(define (draw w)
  (place-image
   (rotate (angle-between (world-pos w) (world-mouse-pos w))
           (if (is-even (round (world-time w))) pic1 pic2))
   (posn-x (world-pos w))
   (posn-y (world-pos w))
   (empty-scene 400 400)))

(define (tick w)
  (make-world
   (world-pos w)
   (world-mouse-pos w)
   (+ 0.1 (world-time w))))

(define (mouse w x y event)
  (make-world
   (world-pos w)
   (make-posn x y)
   (world-time w)
   ))

(big-bang
 initial-world
 (on-draw draw)
 (on-tick tick)
 (on-mouse mouse)
 )
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20110201/380924ae/attachment.html>

Posted on the users mailing list.