Squeak
  links to this page:    
View this PageEdit this PageUploads to this PageHistory of this PageTop of the SwikiRecent ChangesSearch the SwikiHelp Guide
Morphic Animation
Last updated at 12:46 pm UTC on 16 January 2006
I recently received an email:

 Professor Morphic,
  I want to impress my friends and confuse my enemies. Can you teach me 
 Morphic animation techniques?
  Signed,
   Johnny

The quick answer to that is NO. N. O. No. I don't know you. You may be trying to use the power of Morphic as a weapon against the forces of good. I know your type Johnny. You'll just take it and ruin it in some sick and twisted way. How do you think Bill Gates got started? On the other hand, if you're going to use it to try to pick up chicks, then I know you're really pathetic. Morphic will not make you smarter, or better looking. However, it can make you a better person. I think we all would appreciate that.

But I WILL show you how I use Morphic animation. My heart is pure. If you read between the lines, maybe, just maybe, you will learn how to do Morph anime.

So let's go through an example. There are several ways to do this, but I'm just going to give you some code. It's available as the changeset PathRectangle.cs pathrectangle.cs

I'll preface this by saying what I want the animation to do. The Morph starts out as a small 8x8 rectangle. Over time, it grows into a rectangle the size of the instance variable finishRectangle. Also, the animation starts at point beginPoint, and ends up at the location defined by finishRectangle. The rectangle follows the path of an arc that this Morph calculates before it starts animating. So it will appear as if a growing rectangle is following an arc on the display.

First, we design the class that we want.

RectangleMorph subclass: #PathRectangleMorph
instanceVariableNames: 'beginPoint finishRectangle counter increment path '
classVariableNames: ''
poolDictionaries: ''
category: 'Morphic Documentation example'

Instance variable 'counter' tells us where we are in the animation sequence, and 'increment' tells us how much larger we should make the rectangle on each step.

"This goes in as a class method for instance creation"
beginPoint: thePoint finishRectangle: theRectangle
  ^ self new beginPoint: thePoint finishRectangle: theRectangle.

"Everything else are instance methods"

beginPoint: thePoint finishRectangle: theRectangle
  beginPoint _ thePoint.
  finishRectangle _ theRectangle.
  "calculate the path that animation follows "
  self calculatePath.

calculatePath
  |  pathRect pt1 pt2 pt3 pa pb k |

  pathRect _ beginPoint corner: ( finishRectangle topLeft ).
  pt1 _ beginPoint.
  pt2 _ pathRect topRight.
  pt3 _ finishRectangle topLeft.
  path _ Path new.
  path add: pt1.
  pa _ pt2 - pt1.
  pb _ pt3 - pt2.
  k _ 5 max: pa x abs + pa y abs + pb x abs + pb y abs // 20.
  "k is a guess as to how many line segments to use to approximate the curve."
  k _ k/2.
  1 to: k do: 
    [:i | path add: pa * i // k + pt1 * (k - i) + (pb * (i - 1) // k + pt2 * (i - 1)) // (k - 1)].
  path add: pt3.
  " Now calculate the amount to increment the rectangle when we grow"
  increment _ (  finishRectangle extent  - (8 @ 8) ) / ( path size * 2 ).
  increment _ increment max: ( 1 @ 1 ).
  increment _ increment rounded.
  "And place us at the starting line "
  self position: ( path at: 1 ).
This is just like out first example. Not very pretty, but all it does is calculate a path.

initialize
 super initialize.
 "The rectangle is darkGray"
 self color: ( Color darkGray ).
 counter _ 1.
 self borderWidth: 0.
 " set the size to be 8 x 8 "
 self extent: ( 8 @ 8).

reset
 " go back to where I started the animation at "
 counter _ 1.
 self extent: ( 8 @ 8).
 self position: beginPoint.

startStepping
 " add me to the world "
 World addMorphFront: self.
 super startStepping.

step
 | rect1 |
 self position: ( path at: counter ).  
 rect1 _ self bounds expandBy: increment.
 self bounds: (rect1 origin extent: ( rect1 extent min: ( finishRectangle extent ))). 
 counter _ counter + 1.
 "If we've run through the entire path, we're done"
 counter > ((path size) - 1) ifTrue: [ self stopStepping ].

stepTime
 " So that we can see it on the display"
 ^ 200.

stopStepping
 | sender |
 " delete removes me from the world that I entered when I started stepping "
 self delete.
 super stopStepping.
 self reset.
 " and tell my sender that I am done "
 sender _ self valueOfProperty: #sender.
 sender ifNotNil: [ sender doneAnimation].



OK, let's try it out.


bo _ PathRectangleMorph beginPoint: 10@10 finishRectangle: ( 500 @ 400 extent: ( 50 @ 50)).
bo startStepping.


So we see a dull old gray rectangle wander down an arc from the upper left hand of the screen down to the right. Then disappear.

This probably seems very disappointing. Where's the fun, the wow, the [some French word that I can't pronounce, much less spell] ? First, let's crank this puppy. Set stepTime to return 0. Just crank it as fast as we can. In reality, the World that we belong to only will call us every 20 milliseconds, which is 50 frames per second. Try that out.

That's more like it. Mr. Speed. By the way, I'm assuming that you are using at least a PII or G3 class of machine. Animation as beautiful as this deserves the very best hardware.

Turns out that I picked gray for another reason. Remember our friend the window launcher? We're going to hook this animation into him.

Morphic Animation II



Jim Benson
jb@speed.net
The AlignmentMorph may be replaced by any Morph class