Morphic Animation II
Last updated at 5:41 pm UTC on 9 December 2004
When we last left our hero, PathRectangleMorph was busily fighting the forces of evil, preparing the way for all that is good. He had fought himself into a corner. Here comes the LaunchAlignmentMorph to the rescue.
Here's the changeset: anime.cs
LaunchAlignmentMorph instance methods:
animateWindowOpening: theWindow inWorld: theWorld
| topWindow anime |
topWindow _ theWorld firstSubmorph.
topWindow ifNotNil: [
anime _ PathRectangleMorph beginPoint: ( topWindow bounds center ) finishRectangle: ( theWindow bounds).
anime setProperty: #sender toValue: self.
ifNil: [ self doneAnimation ].
" add the world to the window "
world addMorph: window.
" and make the window the frontmost one "
" Make sure that the submorphs are happening "
world startSteppingSubmorphsOf: window.
openInWindowLabeled: aString inWorld: theWorld
| extent |
inset _ 0.
world _ theWorld.
window _ (SystemWindow labelled: aString) model: nil.
" guess at initial extent"
bounds: (RealEstateAgent initialFrameFor: window initialExtent: self fullBounds extent);
addMorph: self frame: (0@0 extent: 1@1);
" calculate extent after adding in case any size related attributes were changed. Use
fullBounds in order to trigger re-layout of layout morphs"
extent _ self fullBounds extent +
(window borderWidth@window labelHeight) + window borderWidth.
window extent: extent.
self animateWindowOpening: window inWorld: theWorld.
So here it is. I've been talking all along about cool this, cool that, blah blah blah blah blah.
Execute this in a Workspace:
I've tried to tell you, but you wouldn't listen. "He's on drugs, he doesn't know what's up!" Now you've seen it. That's way cool stuff. You open a window, and it just doesn't leap up on the screen. Instead the window seems to materialize from somewhere near where you were typing. You can do this three or four times, and the windows show up in different places. Execute the same statement from another location on the screen. Play around with it some and see what it does. You can change the location that the animation starts at in the method #animateWindowOpening:inWorld: For example, you could start at the center of the screen by changing the appropriate line to: anime _ PathRectangleMorph beginPoint: ( World bounds center ) finishRectangle: ( theWindow bounds).
This technique is called temporal aliasing. For those interested in this kind of stuff, there is an excellent article called "Animation: From Cartoons to the User Interface" by Bay-Wei Chang and David Ungar. Ungar was one of the creators of the language Self, the environment where Morphic was first implemented. This article can be found in the "Proceedings of the ACM Symposium on User Interface software and Technology,[UIST] 1993, pg. 45". You can also get it from the ACM Digital Library. Hey, I don't name these things, or the books they come in, I just read 'em.
When I first read that article I remember thinking, "Who are those guys?" and, "Why can't I do things like that?". Well, they had Morphic even way back then. That made them all powerful. They also had real machines, I had toys in comparison. Those guys also told me I needed to use an arc to animate this correctly. They're smart, I believe them. That's why you've had to put up with all that Arc/Path nonsense.
There are a couple of tricky bits I'd like to talk to you about. Remember, I'm telling you right up front, this is tricky. But if you can master the following couple of points, you're well on your way to being the master of all you survey.
When we're getting ready to start the animation we:
anime setProperty: #sender toValue: self.
It's not immediately obvious what this does. Morphs have something called properties You can think of properties (some people call them slots) as extra pockets that a Morph has to carry stuff around in. So we tell the Morph that we're calling anime to put 'self' (which is a LaunchAlignmentMorph) into a pocket named #sender. #sender is an instance of Symbol, which is a fancy kind of String.
You're thinking, "I know this trick. This dude starts talking like this right before he hits me on the side of the head with more of that high falutin' computer jock junk". You know me all too well. Except I'm going to beat you upside the head a couple of times. I can control the power of Morphic, that gives me unbelievable strength.
If we look at the code for PathRectangleMorph #stopStepping, towards the end we see:
sender _ self valueOfProperty: #sender.
sender ifNotNil: [ sender doneAnimation].
We read this code as, "If I have a property called sender, send the message #doneAnimation to the value of the property named sender". After reading that last sentence, you can understand why people came up with programming languages. English just doesn't cut it. The code is easier to read than the description. I guess that's why we don't comment the code either :-)
So why would we want to fiddle around with these property thingies anyway? In our example, here's what we want to happen: The LaunchAlignmentMorph gets ready to open a window. It tells the PathRectangleMorph to show it's animation starting from the center of the window where we're currently at to the top left hand corner of the window that we want to open. When the animation is over, we open the LaunchAlignmentMorph window.
'All good and well', you say. 'Simple'. 'Straight forward'. 'Great, we're done. Ship it'. 'Why is he going to mess with something as simple as that?' The answer is that now we can harness the power of Morphic for all that is good. Right here, right now.
We coded our example to do this: When it is ready to open a window, the LaunchAlignmentMorph says "PathRectangleMorph, you lazy bum, get to work! Start your animation!" This leaves the LaunchAligmentMorph time to do whatever it wants while his minions are out doing their dirty work. (LaunchAlignmentMorphs just love to relax and hang around the pool, you could probably find one there right now if you look).
When the PathRectangleMorph ends the animation sequence, it sends #doneAnimation to the LaunchAlignmentMorph, which tells the LaunchAlignmentMorph that the task that it spun off has completed. Computer nerds have named this process, now get this, an asynchrounous callback. Catchy huh? These are the same people who brought you "http://". You can see why they are the hit of every party.
I'll give you an analogy. Let's say you want a pizza. You go to the pizza place, order a pizza, sit around 20 minutes, get the pizza, and take it home. Computer wizards call this a synchronous process. You start a task and follow it through from beginning to end.
You could have done it differently. You could have called the pizza place on the phone, and ordered the pizza for delivery. Then you could have played your guitar, washed the car, and scared the neighbors cat before the delivery guy brought the pie to your door. You had the time between when you ordered the pizza to when it was actually delivered to do with what you wanted. [Scaring the cat was an excellent idea, I might add]. You weren't actually involved in going to get the pizza, you just started the events in motion to get it delivered to your door. That's an asynchronous process.
As you have figured out by now, that's how we can have all sorts of animations going on at once. We basically just start 'em up and let 'em rip. Plus, the animations have a way to notify someone who is interested in them when they are done animating.
Morphic Animation III
The AlignmentMorph may be replaced by any Morph class