b0bd6094b54436706cf00310ffb99534f811c399
[sdlgit/SDL-Site.git] / pages / SDL-Cookbook-PDL.html-inc
1 <div class="pod">
2 <!-- INDEX START -->
3 <h3 id="TOP">Index</h3>
4
5 <ul><li><a href="#NAME">NAME</a>
6 <ul><li><a href="#CATEGORY">CATEGORY</a></li>
7 </ul>
8 </li>
9 <li><a href="#Old_SDL_interface">Old SDL interface</a></li>
10 <li><a href="#Perl_s_SDL_in_a_nutshell">Perl's SDL in a nutshell</a></li>
11 <li><a href="#SDL_power_through_simplicity">SDL - power through simplicity</a></li>
12 <li><a href="#Example_1_Model_of_a_2_D_Noninteract">Example 1: Model of a 2-D Noninteracting Gas</a>
13 <ul><li><a href="#Computational_Logic">Computational Logic</a></li>
14 <li><a href="#Animation_Logic">Animation Logic</a></li>
15 <li><a href="#Disappearing_Particles_Some_of_the_p">Disappearing Particles!
16 Some of the particles can drift off the screen.  This is no good. What's causing this problem?</a></li>
17 <li><a href="#Disappearing_Particles_take_2">Disappearing Particles, take 2</a></li>
18 </ul>
19 </li>
20 <li><a href="#What_s_in_a_Name_Pesky_conflicts_wit">What's in a Name?  Pesky conflicts with main::in()</a>
21 <ul><li><a href="#Solution_1_Explicit_scoping_using_pa">Solution 1: Explicit scoping using packages</a></li>
22 <li><a href="#Solution_2_Removing_SDL_s_in_or_PDL_">Solution 2: Removing SDL's in or PDL's in from the symbol table</a></li>
23 </ul>
24 </li>
25 <li><a href="#Making_the_simulation_interactive">Making the simulation interactive</a>
26 <ul><li><a href="#Present_state_of_the_code">Present state of the code</a></li>
27 <li><a href="#Listening_to_Events">Listening to Events</a></li>
28 <li><a href="#Responding_to_events">Responding to events</a></li>
29 <li><a href="#Final_State_of_the_Code">Final State of the Code</a></li>
30 </ul>
31 </li>
32 <li><a href="#Directions_for_future_work">Directions for future work</a>
33 </li>
34 </ul><hr />
35 <!-- INDEX END -->
36
37 <h1 id="NAME">NAME</h1><p><a href="#TOP" class="toplink">Top</a></p>
38 <div id="NAME_CONTENT">
39 <p>SDL::CookBook::PDL -- CookBook for SDL + PDL</p>
40 <p>PDL provides great number crunching capabilities to Perl and SDL provides game-developer quality real-time bitmapping and sound.  You can use PDL and SDL ''together'' to create real-time, responsive animations and simulations.  In this section we will go through the pleasures and pitfalls of working with both powerhouse libraries.</p>
41
42
43
44
45
46 </div>
47 <h2 id="CATEGORY">CATEGORY</h2>
48 <div id="CATEGORY_CONTENT">
49 <p>Cookbook</p>
50
51 </div>
52 <h1 id="Old_SDL_interface">Old SDL interface</h1><p><a href="#TOP" class="toplink">Top</a></p>
53 <div id="Old_SDL_interface_CONTENT">
54 <p>Please be aware that much of the code in this example uses SDL Perl v 2.2.4.  The SDL Perl developers are hard at work rewriting SDL, to be released as SDL 3.0 soon.  The new version of SDL is not backwards compatible.  Check back with this page after SDL 3.0 has been released to get the updated commands.</p>
55
56 </div>
57 <h1 id="Perl_s_SDL_in_a_nutshell">Perl's SDL in a nutshell</h1><p><a href="#TOP" class="toplink">Top</a></p>
58 <div id="Perl_s_SDL_in_a_nutshell_CONTENT">
59 <p>SDL stands for Simple DirectMedia Layer.  It's a cross-platform library written in C that's meant to handle all of the low-level graphics and sound stuff.  You can read more about SDL here: http://www.libsdl.org/.  Because SDL is focused on game programming, it has a raw but clean feel to it.  We will focus for now on using SDL to handle images for us.  Handling sound may some day be the focus of another chapter.</p>
60 <p>We will be using Perl's SDL module, not SDL directly.  Fortunately, Perl's SDL module has a small collection of very simple tutorials that perfectly introduce basic usage.  You can find them here: http://sdl.perl.org/tutorials/.  Another excellent and very substantial introduction can be found here: http://arstechnica.com/gaming/news/2006/02/games-perl.ars</p>
61 <p>SDL is not a Perl core module, so you'll need to install it before moving forward.  Before moving on, go through some of the tutorials and play around with SDL a little bit.  Continue on once you think you've got the hang of it.</p>
62
63 </div>
64 <h1 id="SDL_power_through_simplicity">SDL - power through simplicity</h1><p><a href="#TOP" class="toplink">Top</a></p>
65 <div id="SDL_power_through_simplicity_CONTENT">
66 <p>One of the first questions you're bound to ask when you begin using SDL for your own work is, &quot;How do I draw a line?&quot;  As it turns out, you don't!  SDL's pixmap capabilities are just that - pixmap capabilities.  If you want to draw a line, you'll have to do it manually.</p>
67 <p>For example, here is a very poorly implemented hack (read - don't do this at home) that will draw a simple sine-wave graph:</p>
68 <pre>   #!/usr/bin/perl
69         use warnings;
70         use strict;
71
72         use SDL;
73         use SDL::App;
74         use SDL::Rect;
75         use SDL::Color;
76
77         # User defined pen-nib size.
78         my $nib_size = 3;
79
80         # Create the SDL App
81         my $app = SDL::App-&gt;new(
82                 -width  =&gt; 640,
83                 -height =&gt; 480,
84                 -depth  =&gt; 16,
85                 -title  =&gt; &quot;Hello, World!&quot;,
86         );
87
88         # our nib will be white
89         my $nib_color = SDL::Color-&gt;new(
90                         -r =&gt; 0xff,
91                         -g =&gt; 0xff,
92                         -b =&gt; 0xff,
93                 );
94
95         # and our nib will be represented by a rectangle
96         # (Alternatively, you could use an image, which would allow
97         # for pretty anti-aliasing if you created it in GIMP with
98         # antialiasing)
99         my $nib = SDL::Rect-&gt;new(
100                 -height =&gt; $nib_size,
101                 -width  =&gt; $nib_size,
102         );
103
104         # now draw a sine wave (wthout covering up previously drawn rectangles)
105         my $t = 0;
106         my $t_step = 2**-4;
107         for($t = 0; $t &lt; 50; $t += $t_step) {
108                 $nib-&gt;x( $t * 8 );
109                 $nib-&gt;y( sin($t) * 80 + 240 );
110
111                 $app-&gt;fill( $nib, $nib_color );
112         }
113
114         # Generally use the update command, but if you want to update the whole
115         # surface, use flip
116         $app-&gt;flip()
117
118         sleep 5;
119
120 </pre>
121 <p>Wait a second, you say, this doesn't seem either powerful or simple!  You're right, but that's not because SDL is a poor tool.  Rather, this example targets SDL's weaknesses rather than its strenghts.</p>
122 <p>If you need to make a plot, use PLplot or PGPLOT.  If you need to make something move, use SDL.</p>
123
124 </div>
125 <h1 id="Example_1_Model_of_a_2_D_Noninteract">Example 1: Model of a 2-D Noninteracting Gas</h1><p><a href="#TOP" class="toplink">Top</a></p>
126 <div id="Example_1_Model_of_a_2_D_Noninteract-2">
127 <p>In this section we'll develop a fully working animation/simulation.  We'll start with something quite simple for now and expand it as we go along.  The goal of this example is for it to work, not to be well-designed.  For a discussion of making your simulations well-designed, read below.</p>
128 <p>We will separate our program into two parts: the computational logic and the animation logic.  Here's the introduction and the computational part:</p>
129
130 </div>
131 <h2 id="Computational_Logic">Computational Logic</h2>
132 <div id="Computational_Logic_CONTENT">
133 <pre>
134
135
136         #!/usr/bin/perl
137         # A simple simulation
138         use warnings;
139         use strict;
140         use PDL;
141
142         # Set up the system parameters, including random positions and velocities.
143         my $d_t = 2**-3;
144         my $side_length = 200;
145         my $numb_of_atoms = 100;
146         my $positions = random(2, $numb_of_atoms) * $side_length;
147         my $velocities = random(2, $numb_of_atoms) * 6;
148
149         sub compute {
150                 $positions += $d_t * $velocities;
151         }
152
153 </pre>
154 <p>If you've ever written a simulation, you'll probably object that we don't have any iteration over time.  You're right, but it turns out that the timing works much better in SDL's event loop than in our computational logic.  The purpose of the computational logic is to let us focus on encoding our systems dynamics without having to worry about the application logic.  In this case, the computational logic simply updates the positions of the particles according to their velocities.</p>
155
156 </div>
157 <h2 id="Animation_Logic">Animation Logic</h2>
158 <div id="Animation_Logic_CONTENT">
159 <p>We next need to figure out how the application is actually going to run and display anything.  We'll do this in two stages, the application intialization and the run loop.</p>
160 <p>Here's some initialization code to get started; put this below the code already supplied above:</p>
161 <pre>   use SDL;
162         use SDL::App;
163         use SDL::Rect;
164         use SDL::Color;
165
166         # Create the SDL App
167         my $app = SDL::App-&gt;new( -width  =&gt; $side_length, -height =&gt; $side_length, 
168                                         -title  =&gt; &quot;Simple Simulation!&quot;, -depth  =&gt; 16, );
169
170         # white particles on a black background
171         my $particle_color = SDL::Color-&gt;new( -r =&gt; 0xff, -g =&gt; 0xff, -b =&gt; 0xff, );
172         my $bg_color = SDL::Color-&gt;new( -r =&gt; 0x00, -g =&gt; 0x00, -b =&gt; 0x00, );
173
174         # rectangles for the particles and the background
175         my $particle = SDL::Rect-&gt;new( -height =&gt; 5, -width  =&gt; 5, );
176         my $bg = SDL::Rect-&gt;new( -height =&gt; $side_length, -width =&gt; $side_length, );
177
178 </pre>
179 <p>Hopefully this is straightforward code.  We pull in our library dependencies and then create a few objects with the necessary properties.  Finally, we get to the actual application loop:</p>
180 <pre>   # Run the simulation by (1) computing the updated positions, clearing the canvas, drawing the
181         # new particles, updating the visual display, and pausing before continuing:
182         for(my $t = 0; $t &lt; 20; $t += $d_t) {
183                 compute();
184
185                 # Clean the canvas
186                 $app-&gt;fill( $bg, $bg_color);
187                 for(my $i = 0; $i &lt; $numb_of_atoms; $i++) {
188                         $particle-&gt;x( $positions-&gt;at(0,$i) );
189                         $particle-&gt;y( $positions-&gt;at(1,$i) );
190                         $app-&gt;fill( $particle, $particle_color );
191                 }
192                 $app-&gt;flip();
193                 $app-&gt;delay(10);
194         }
195
196 </pre>
197 <p>When you run this code (combined with the code already supplied), you should get a bunch of particles slowly drifting down and to the right.  Not all that interesting, but then again, we have a simulation up and working!  Cool!.</p>
198
199 </div>
200 <h2 id="Disappearing_Particles_Some_of_the_p">Disappearing Particles!
201 Some of the particles can drift off the screen.  This is no good. What's causing this problem?</h2>
202 <div id="Disappearing_Particles_Some_of_the_p-2">
203 <p>The root of the problem is that our computational code is, well, rather dumb, it doesn't check to see if the particle is about to go off the screen.  So, we need to update our computational code to look like this:</p>
204 <pre>   sub compute {
205                 $positions += $d_t * $velocities;
206
207                 # Find all particles that are 'outside' the box, place them back in
208                 # box, and reverse their directions
209                 my ($bad_pos, $bad_vel)
210                         = where($positions, $velocities, $positions &gt; $side_length);
211                 $bad_vel *= -1;
212                 $bad_pos .= 2 * $side_length - $bad_pos;
213         }
214
215 </pre>
216 <p>With this change to the code, you should get particles that 'bounce' when the reach the far edge.  This is far from satisfactory, however, because the compute code is adjusting the particle's ''left'' edge, not its center, so the particles nearly go off the screen before they bounce.  To fix this, we work with an effective side length instead:</p>
217 <pre>   my $effective_length = $side_length - 5;
218         sub compute {
219                 $positions += $d_t * $velocities;
220
221                 # Find all particles that are 'outside' the box and push them back in the
222                 # opposite direction, reversing their directions, too.
223                 my ($bad_pos, $bad_vel)
224                         = where($positions, $velocities, $positions &gt; $effective_length);
225                 $bad_vel *= -1;
226                 $bad_pos .= 2 * $effective_length - $bad_pos;
227         }
228
229 </pre>
230 <p>So far I've been carrying that explicit constant of 5 to represent the size of the particles.  We should put that in a variable somewhere so that it's a bitcode&gt; and put it near the top.  Also, the velocities are rather silly - we don't have any negative velocities.  Let's try using &lt;code&gt;grandom&lt;/code&gt; instead.  Now your variable initialization code should look something like this:</p>
231 <pre>   # Set up the system parameters, including random positions and velocities.
232         my $d_t = 2**-3;
233         my $side_length = 200;
234         my $particle_size = 5;
235         my $numb_of_atoms = 100;
236         my $positions = random(2, $numb_of_atoms) * $side_length;
237         my $velocities = grandom(2, $numb_of_atoms) * 5;
238
239 </pre>
240
241 </div>
242 <h2 id="Disappearing_Particles_take_2">Disappearing Particles, take 2</h2>
243 <div id="Disappearing_Particles_take_2_CONTEN">
244 <p>Unless you experience an unusual circumstance, all of the particles will quickly shrivel up and disappear!  What's going on?  It turns out we have a problem with our computational logic again, but we are also running into strange behavior from SDL.  We'll take a look at SDL's weird behavior first.</p>
245 <p>Clearly the particle rectangle's size is not supposed to change, but somehow it does.  To confince yourself of this, modify the &lt;code&gt;for&lt;/code&gt; loop in the application loop so it looks more like this, which explicitly sets the particle size for every particle that's drawn:</p>
246 <pre>   for(my $i = 0; $i &lt; $numb_of_atoms; $i++) {
247                 $particle-&gt;x( $positions-&gt;at(0,$i) );
248                 $particle-&gt;y( $positions-&gt;at(1,$i) );
249                 $particle-&gt;height( $particle_size );
250                 $particle-&gt;width( $particle_size );
251                 $app-&gt;fill( $particle, $particle_color );
252         }
253
254 </pre>
255 <p>Now it's clear that although we still have particles flying off the screen up and to the left, they are no longer shriveling away.  This strange behavior is due to SDL's response to a negative position for a rectangle - it just resizes the rectangle so that it only the portion of the rectangle that's in positive territory remains.  The upshot is that you must always be careful about how you handle drawing positons.</p>
256 <p>Now that the particles are no longer disappearing, it's clear that we forgot to set up a physical boundary condition for our particles on the uppper and left edges.  To fix that, we modify the compute function:</p>
257 <pre>   sub compute {
258                 $positions += $d_t * $velocities;
259
260                 # Find all particles that are 'outside' the box and push them back in the
261                 # opposite direction, reversing their directions, too.
262                 my ($bad_pos, $bad_vel)
263                         = where($positions, $velocities, $positions &gt; $effective_length);
264                 $bad_vel *= -1;
265                 $bad_pos .= 2 * $effective_length - $bad_pos;
266
267                 ($bad_pos, $bad_vel) = where($positions, $velocities, $positions &lt; 0);
268                 $bad_vel *= -1;
269                 $bad_pos *= -1;
270         }
271
272 </pre>
273 <p>You can also remove the explicit particle-sizing that we put in before, because it's no longer a problem.</p>
274 <p>And there you have it!  We have a fully fledged simulation of noninteracting particles in a box!</p>
275
276 </div>
277 <h1 id="What_s_in_a_Name_Pesky_conflicts_wit">What's in a Name?  Pesky conflicts with main::in()</h1><p><a href="#TOP" class="toplink">Top</a></p>
278 <div id="What_s_in_a_Name_Pesky_conflicts_wit-2">
279 <p>If you've been running your simulations along with the demo, you'll almost certainly have noticed an error looking something like this:</p>
280 <pre> Prototype mismatch: sub main::in (;@) vs none at ./sdlsandbox.pl line 36
281
282 </pre>
283 <p>This is the unfortunate consequence of both SDL and PDL exporting their &lt;code&gt;in&lt;/code&gt; function to their enclosing namespace.  The standard solution to this is to have modify one of your &lt;code&gt;use&lt;/code&gt; lines so it looks like </p>
284 <pre>    use PDL qw( !in );
285
286 </pre>
287 <p>Unfortunately, PDL doesn't listen you what you say when it imports functions into the namespace.  As far as I can tell, neither does SDL.  The best way to fix this problem is to encapsulate one of the two pieces of code into its own package.  We'll do that with the MyComputation package.</p>
288
289 </div>
290 <h2 id="Solution_1_Explicit_scoping_using_pa">Solution 1: Explicit scoping using packages</h2>
291 <div id="Solution_1_Explicit_scoping_using_pa-2">
292 <p>Tweak your code a bit so that you call <code>use PDL;</code> within the MyCompute package, and place all of the piddles within that package space:</p>
293 <pre>   package MyCompute;
294         use PDL;
295         my $positions = random(2, $numb_of_atoms) * $side_length;
296
297         # ... and later
298         package main;
299         use SDL;
300
301         # ... and later, tweak the application loop
302         for(my $t = 0; $t &lt; 20; $t += $d_t) {
303                 MyCompute::compute();
304
305 </pre>
306 <p>And now everything should run fine, without any more warnings!</p>
307
308 </div>
309 <h2 id="Solution_2_Removing_SDL_s_in_or_PDL_">Solution 2: Removing SDL's in or PDL's in from the symbol table</h2>
310 <div id="Solution_2_Removing_SDL_s_in_or_PDL_-2">
311 <p>Sometimes you have to mix your animation code with computational code, in which case the above solution doesn't solve your problem.  If you find that you don't need to use one of PDL's or SDL's &lt;code&gt;in&lt;/code&gt; function in your own code, go ahead and remove it from the main symbol table.  You can always get back to it later by fully qualifying the function call.  To remove SDL's &lt;code&gt;in&lt;/code&gt; function, use code like this:</p>
312 <pre>   # use SDL, but remove SDL's in function before loading PDL
313         use SDL;
314         BEGIN {
315                 delete $main::{in};
316         }
317         use PDL;
318
319 </pre>
320 <p>If you would rather have SDL's <code>in</code> function in your main symbol table, reverse the placement of &lt;code&gt;use SD<a href="#code">code</a> and &lt;code&gt;use PD<a href="#code">code</a> in the previous example:</p>
321 <pre>   # use PDL, but remove its 'in' function before loading SDL
322         use PDL;
323         BEGIN {
324                 delete $main::{in};
325         }
326         use SDL;
327
328 </pre>
329
330 </div>
331 <h1 id="Making_the_simulation_interactive">Making the simulation interactive</h1><p><a href="#TOP" class="toplink">Top</a></p>
332 <div id="Making_the_simulation_interactive_CO">
333 <p>As the closing portion of this chapter, we'll consider how to make the simulation interactive.  SDL captures keyboard and mouse behavior, so putting this into our simulator is straightforward.</p>
334
335 </div>
336 <h2 id="Present_state_of_the_code">Present state of the code</h2>
337 <div id="Present_state_of_the_code_CONTENT">
338 <p>Before moving into getting user interaction, I first want to be sure we're working with the same code.  In particular, I've made a couple of important modifications so that this code is slightly different from what we were working with above.  I'll point out those differences as we come to them.  Here's the program as it stands, from top to bottom:</p>
339 <pre>   #!/usr/bin/perl
340         # A simple simulation
341         use warnings;
342         use strict;
343
344         ## Global Variables ##
345
346         # Set up the system parameters, including random positions and velocities.
347         my $d_t = 2**-3;
348         my $side_length = 200;
349         my $particle_size = 5;
350         my $numb_of_atoms = 100;
351
352         ## Computational Stuff ##
353
354         package MyCompute;
355         use PDL;
356         my $positions = random(2, $numb_of_atoms) * $side_length;
357         my $velocities = grandom(2, $numb_of_atoms) * 6;
358         my $effective_length;
359
360         sub compute {
361                 my $effective_length = $side_length - $particle_size;
362
363                 # update the a real simulation, this is the interesting part
364                 $positions += $d_t * $velocities;
365
366                 # Check boundary conditions.  Find all particles that are 'outside' the box,
367                 # place them back in the box, and reverse their directions
368                 my ($bad_pos, $bad_vel)
369                         = where($positions, $velocities, $positions &gt; $effective_length);
370                 $bad_vel *= -1;
371                 $bad_pos .= 2 * $effective_length - $bad_pos;
372
373                 ($bad_pos, $bad_vel) = where($positions, $velocities, $positions &lt; 0);
374                 $bad_vel *= -1;
375                 $bad_pos *= -1;
376         }
377
378
379
380
381         ## Animation Code ##
382
383         package main;
384
385         use SDL;
386         use SDL::App;
387         use SDL::Rect;
388         use SDL::Color;
389
390         # Create the SDL App
391         my $app = SDL::App-&gt;new( -width  =&gt; $side_length, -height =&gt; $side_length, 
392                                 -title  =&gt; &quot;Simple Simulation!&quot;, -depth  =&gt; 16, );
393
394         # white particles on a black background
395         my $particle_color = SDL::Color-&gt;new( -r =&gt; 0xff, -g =&gt; 0xff, -b =&gt; 0xff, );
396         my $bg_color = SDL::Color-&gt;new( -r =&gt; 0x00, -g =&gt; 0x00, -b =&gt; 0x00, );
397
398         # rectangles for the particles and the background
399         my $particle = SDL::Rect-&gt;new( -height =&gt; 5, -width  =&gt; 5, );
400         my $bg = SDL::Rect-&gt;new( -height =&gt; $side_length, -width =&gt; $side_length, );
401
402         # Run the simulation
403         for(my $t = 0; $t &lt; 20; $t += $d_t) {
404                 MyCompute::compute();
405
406                 # Clean the canvas
407                 $app-&gt;fill( $bg, $bg_color);
408                 for(my $i = 0; $i &lt; $numb_of_atoms; $i++) {
409                         $particle-&gt;x( $positions-&gt;at(0,$i) );
410                         $particle-&gt;y( $positions-&gt;at(1,$i) );
411                         $app-&gt;fill( $particle, $particle_color );
412                 }
413                 $app-&gt;flip();
414                 $app-&gt;delay(10);
415         }
416
417 </pre>
418 <p>So there it is, top to bottom, in about 75 lines.</p>
419
420 </div>
421 <h2 id="Listening_to_Events">Listening to Events</h2>
422 <div id="Listening_to_Events_CONTENT">
423 <p>To respond to user interactions, we have to listen to user events using an SDL::Event object.  So first, add this line with our other use statements:</p>
424 <pre> use SDL::Event;
425
426 </pre>
427 <p>and then be sure to create an event object amongst the animation initialization code:</p>
428 <pre> my $event = new SDL::Event;
429
430 </pre>
431 <p>Finally, we need to update the application loop so that it examines and responds to events.  Replace the current application loop with this code:</p>
432 <pre>   # Run the simulation
433         while(1) {
434                 MyCompute::compute();
435
436                 # Clean the canvas
437                 $app-&gt;fill( $bg, $bg_color);
438                 for(my $i = 0; $i &lt; $numb_of_atoms; $i++) {
439                         $particle-&gt;x( $positions-&gt;at(0,$i) );
440                         $particle-&gt;y( $positions-&gt;at(1,$i) );
441                         $app-&gt;fill( $particle, $particle_col10);
442
443                 while($event-&gt;poll()) {
444                         if($event-&gt;type() =head1  SDL_QUIT) {
445                                 exit;
446                         }
447                 }
448         }
449
450 </pre>
451 <p>Now the animator will run indefinitely, until you explicitly tell it to close.  (You may have noticed before that the application would not close even if you told it to close.  Now we've fixed that.)</p>
452
453 </div>
454 <h2 id="Responding_to_events">Responding to events</h2>
455 <div id="Responding_to_events_CONTENT">
456 <p>When SDL gets a mouse response or a keyboard key press, it tells you with an event.  The naive way to process these event is with a series of if statements.  Don't do that.</p>
457 <p>Instead, create a dispatch table, which is nothing more than a hash whose values are the subroutines you want to have run when an event happens.  Replace the application loop with the following code:</p>
458 <pre>   # event dispatch table
459         my $keyname_dispatch_table = {
460                 'up'    =&gt; \&amp;incr_particle_size, # up key makes particles larger
461                 'down'  =&gt; \&amp;decr_particle_size, # down key makes particles smaller
462                 'space' =&gt; sub { $d_t = -$d_t        },      # space-bar reverses time
463                 '.'     =&gt; sub { $d_t *= 1.1 },      # right-angle-bracket fast-forwards
464                 ','     =&gt; sub { $d_t /= 1.1 },      # left-angle-bracket slows down
465                 'q'     =&gt; sub { exit;               },      # q exits
466         };
467
468         sub incr_particle_size {
469                 $particle_size++;
470                 $particle-&gt;height($particle_size);
471                 $particle-&gt;width($particle_size);
472         }
473
474         sub decr_particle_size {
475                 $particle_size-- if $particle_size &gt; 1;
476                 $particle-&gt;height($particle_size);
477                 $particle-&gt;width($particle_size);
478         }
479
480
481
482
483         # Run the simulation
484         while(1) {
485                 MyCompute::compute();
486
487                 # Clean the canvas
488                 $app-&gt;fill( $bg, $bg_color);
489                 for(my $i = 0; $i &lt; $numb_of_atoms; $i++) {
490                         $particle-&gt;x( $positions-&gt;at(0,$i) );
491                         $particle-&gt;y( $positions-&gt;at(1,$i) );
492                         $app-&gt;fill( $particle, $particle_color );
493                 }
494                 $app-&gt;flip();
495                 $app-&gt;delay(10);
496
497                 while($event-&gt;poll()) {
498                         if($event-&gt;type() =head1  SDL_QUIT) {
499                                 exit;
500                         } elsif($event-&gt;type() =head1  SDL_KEYDOWN) {
501                                 if(exists $keyname_dispatch_table-&gt;{$event-&gt;key_name()}) {
502                                         $keyname_dispatch_table-&gt;{$event-&gt;key_name()}-&gt;();
503                                 }
504                         }
505                 }
506         }
507
508 </pre>
509 <p>Dispatch tables allow for powerful methods of abstracting your program logic.  Now adding a new event handler is as easy as updating the dispatch table!</p>
510 <p>As written, you can now increase or decrease the particle size using the up and down arrow keys, you can increase ory using the right or left angle-brackets, you can reverse time using the space bar, or you can quit by pressing q.</p>
511
512 </div>
513 <h2 id="Final_State_of_the_Code">Final State of the Code</h2>
514 <div id="Final_State_of_the_Code_CONTENT">
515 <p>Just so that you've got a complete working example, here is the final state of the code, clocking in at a mere 115 lines:</p>
516 <pre>   #!/usr/bin/perl
517         # A simple simulation
518         use warnings;
519         use strict;
520
521         ## Global Variables ##
522
523         # Set up the system parameters, including random positions and velocities.
524         my $d_t = 2**-3;
525         my $side_length = 200;
526         my $particle_size = 5;
527         my $numb_of_atoms = 100;
528
529         ## Computational Stuff ##
530
531         package MyCompute;
532         use PDL;
533         my $positions = random(2, $numb_of_atoms) * $side_length;
534         my $velocities = grandom(2, $numb_of_atoms) * 6;
535         my $effective_length;
536
537         sub compute {
538                 my $effective_length = $side_length - $particle_size;
539
540                 # update the positions.  For a real simulation, this is the interesting part
541                 $positions += $d_t * $velocities;
542
543                 # Check boundary conditions.  Find all particles that are 'outside' the box,
544                 # place them back in the box, and reverse their directions
545                 my ($bad_pos, $bad_vel)
546                         = where($positions, $velocities, $positions &gt; $effective_length);
547                 $bad_vel *= -1;
548                 $bad_pos .= 2 * $effective_length - $bad_pos;
549
550                 ($bad_pos, $bad_vel) = where($positions, $velocities, $positions &lt; 0);
551                 $bad_vel *= -1;
552                 $bad_pos *= -1;
553         }
554
555
556
557
558         ## Animation Code ##
559
560         package main;
561
562         use SDL;
563         use SDL::App;
564         use SDL::Rect;
565         use SDL::Color;
566         use SDL::Event;
567
568         # Create the SDL App
569         my $app = SDL::App-&gt;new( -width  =&gt; $side_length, -height =&gt; $side_length, 
570                                 -title  =&gt; &quot;Simple Simulation!&quot;, -depth  =&gt; 16, );
571
572         # white particles on a black background
573         my $particle_color = SDL::Color-&gt;new( -r =&gt; 0xff, -g =&gt; 0xff, -b =&gt; 0xff, );
574         my $bg_color = SDL::Color-&gt;new( -r =&gt; 0x00, -g =&gt; 0x00, -b =&gt; 0x00, );
575
576         # rectangles for the particles and the background
577         my $particle = SDL::Rect-&gt;new( -height =&gt; 5, -width  =&gt; 5, );
578         my $bg = SDL::Rect-&gt;new( -height =&gt; $side_length, -width =&gt; $side_length, );
579
580         # event listener
581         my $event = new SDL::Event;
582
583         # event dispatch table
584         my $keyname_dispatch_table = {
585                 'up'    =&gt; \&amp;incr_particle_size, # up key makes particles large
586                 'down'  =&gt; \&amp;decr_particle_size, # up key makes particles large
587                 'space' =&gt; sub { $d_t = -$d_t        },      
588                 '.'     =&gt; sub { $d_t *= 1.1 },      # right-arrow fast-forwards
589                 ','     =&gt; sub { $d_t /= 1.1 },      # left-arrow slows down
590                 'q'     =&gt; sub { exit;               },      # q exits
591         };
592
593         sub incr_particle_size {
594                 $particle_size++;
595                 $particle-&gt;height($particle_size);
596                 $particle-&gt;width($particle_size);
597         }
598
599         sub decr_particle_size {
600                 $particle_size-- if $particle_size &gt; 1;
601                 $particle-&gt;height($particle_size);
602                 $particle-&gt;width($particle_size);
603         }
604
605
606
607
608         # Run the simulation
609         while(1) {
610                 MyCompute::compute();
611
612                 # Clean the canvas
613                 $app-&gt;fill( $bg, $bg_color);
614                 for(my $i = 0; $i &lt; $numb_of_atoms; $i++) {
615                         $particle-&gt;x( $positions-&gt;at(0,$i) );
616                         $particle-&gt;y( $positions-&gt;at(1,$i) );
617                         $app-&gt;fill( $particle, $particle_color );
618                 }
619                 $app-&gt;flip();
620                 $app-&gt;delay(10);
621
622                 while($event-&gt;poll()) {
623                         if($event-&gt;type() =head1  SDL_QUIT) {
624                                 exit;
625                         } elsif($event-&gt;type() =head1  SDL_KEYDOWN) {
626                                 if(exists $keyname_dispatch_table-&gt;{$event-&gt;key_name()}) {
627                                         $keyname_dispatch_table-&gt;{$event-&gt;key_name()}-&gt;();
628                                 }
629                         }
630                 }
631         }
632
633 </pre>
634 <p>Next, if you want to model interactions among particles, you could write code in the compute function to handle that for you.  If you wanted to use little balls instead of the boxes we've used here, you could create your own images and use an SDL::Surface to load the image.  You can't resize an image using SDL, but then you'd probably be working with real interactions anyway, like a Coulomb force, in which case you'd really be adjusting the interaction strength, not the particle size.</p>
635
636 </div>
637 <h1 id="Directions_for_future_work">Directions for future work</h1><p><a href="#TOP" class="toplink">Top</a></p>
638 <div id="Directions_for_future_work_CONTENT">
639 <p>I have a couple of ideas for future work combining PDL and SDL.</p>
640 <dl>
641         <dt>PLplot driver thingy that creates plots that can be blitted onto an app.  This way, having a graph plotting along side your simulation would be straightforward.
642 =item  Write a function to convert SDL::Surface to a collection of rgba piddles.  We might even be able to convince the piddle to work directly with the memory allocated for the SDL::Survace object for super-fast PDL-based image manipulations.  As an added bonus, you'd be able to slice and dice!</dt>
643 </dl>
644
645 </div>
646 </div>