updated docs and path to my repo
[sdlgit/SDL-Site.git] / pages / SDL-Cookbook-PDL.html-inc
CommitLineData
162a0989 1<div class="pod">
2<!-- INDEX START -->
3<h3 id="TOP">Index</h3>
4
879e3e79 5<ul><li><a href="#NAME">NAME</a>
60f74f6f 6<ul><li><a href="#CATEGORY">CATEGORY</a></li>
7</ul>
8</li>
162a0989 9<li><a href="#Perl_s_SDL_in_a_nutshell">Perl's SDL in a nutshell</a></li>
10<li><a href="#SDL_power_through_simplicity">SDL - power through simplicity</a></li>
11<li><a href="#Example_1_Model_of_a_2_D_Noninteract">Example 1: Model of a 2-D Noninteracting Gas</a>
12<ul><li><a href="#Computational_Logic">Computational Logic</a></li>
13<li><a href="#Animation_Logic">Animation Logic</a></li>
14<li><a href="#Disappearing_Particles_Some_of_the_p">Disappearing Particles!
15Some of the particles can drift off the screen. This is no good. What's causing this problem?</a></li>
16<li><a href="#Disappearing_Particles_take_2">Disappearing Particles, take 2</a></li>
17</ul>
18</li>
19<li><a href="#What_s_in_a_Name_Pesky_conflicts_wit">What's in a Name? Pesky conflicts with main::in()</a>
20<ul><li><a href="#Solution_1_Explicit_scoping_using_pa">Solution 1: Explicit scoping using packages</a></li>
21<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>
22</ul>
23</li>
24<li><a href="#Making_the_simulation_interactive">Making the simulation interactive</a>
25<ul><li><a href="#Present_state_of_the_code">Present state of the code</a></li>
26<li><a href="#Listening_to_Events">Listening to Events</a></li>
27<li><a href="#Responding_to_events">Responding to events</a></li>
28<li><a href="#Final_State_of_the_Code">Final State of the Code</a></li>
29</ul>
30</li>
31<li><a href="#Directions_for_future_work">Directions for future work</a>
32</li>
33</ul><hr />
34<!-- INDEX END -->
35
879e3e79 36<h1 id="NAME">NAME</h1><p><a href="#TOP" class="toplink">Top</a></p>
37<div id="NAME_CONTENT">
38<p>SDL::CookBook::PDL -- CookBook for SDL + PDL</p>
162a0989 39<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>
879e3e79 40
41
42
43
44
45</div>
60f74f6f 46<h2 id="CATEGORY">CATEGORY</h2>
47<div id="CATEGORY_CONTENT">
48<p>Cookbook</p>
49
50</div>
162a0989 51<h1 id="Perl_s_SDL_in_a_nutshell">Perl's SDL in a nutshell</h1><p><a href="#TOP" class="toplink">Top</a></p>
52<div id="Perl_s_SDL_in_a_nutshell_CONTENT">
162a0989 53<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>
54<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>
55<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>
56
57</div>
58<h1 id="SDL_power_through_simplicity">SDL - power through simplicity</h1><p><a href="#TOP" class="toplink">Top</a></p>
59<div id="SDL_power_through_simplicity_CONTENT">
60<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>
61<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>
62<pre> #!/usr/bin/perl
63 use warnings;
64 use strict;
65
66 use SDL;
67 use SDL::App;
68 use SDL::Rect;
69 use SDL::Color;
1f5d8082 70 use SDL::Video;
162a0989 71
72 # User defined pen-nib size.
73 my $nib_size = 3;
74
75 # Create the SDL App
76 my $app = SDL::App-&gt;new(
77 -width =&gt; 640,
78 -height =&gt; 480,
79 -depth =&gt; 16,
80 -title =&gt; &quot;Hello, World!&quot;,
81 );
82
83 # our nib will be white
84 my $nib_color = SDL::Color-&gt;new(
1f5d8082 85 0xff, #r
86 0xff, #b
87 0xff, #g
162a0989 88 );
89
90 # and our nib will be represented by a rectangle
91 # (Alternatively, you could use an image, which would allow
92 # for pretty anti-aliasing if you created it in GIMP with
93 # antialiasing)
1f5d8082 94 my $nib = SDL::Rect-&gt;new(
95 0 , 0, #x, y
96 $nib_size, #w
97 $nib_size, #h
162a0989 98 );
99
100 # now draw a sine wave (wthout covering up previously drawn rectangles)
101 my $t = 0;
102 my $t_step = 2**-4;
103 for($t = 0; $t &lt; 50; $t += $t_step) {
104 $nib-&gt;x( $t * 8 );
105 $nib-&gt;y( sin($t) * 80 + 240 );
106
1f5d8082 107 SDL::Video::fill_rect($app, $nib, $nib_color );
162a0989 108 }
109
110 # Generally use the update command, but if you want to update the whole
111 # surface, use flip
1f5d8082 112 SDL::Video::fill_rect($app,)
162a0989 113
114 sleep 5;
115
116</pre>
117<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>
118<p>If you need to make a plot, use PLplot or PGPLOT. If you need to make something move, use SDL.</p>
119
120</div>
121<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>
122<div id="Example_1_Model_of_a_2_D_Noninteract-2">
123<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>
124<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>
125
126</div>
127<h2 id="Computational_Logic">Computational Logic</h2>
128<div id="Computational_Logic_CONTENT">
129<pre>
130
131
132 #!/usr/bin/perl
133 # A simple simulation
134 use warnings;
135 use strict;
136 use PDL;
137
138 # Set up the system parameters, including random positions and velocities.
139 my $d_t = 2**-3;
140 my $side_length = 200;
141 my $numb_of_atoms = 100;
142 my $positions = random(2, $numb_of_atoms) * $side_length;
143 my $velocities = random(2, $numb_of_atoms) * 6;
144
145 sub compute {
146 $positions += $d_t * $velocities;
147 }
148
149</pre>
150<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>
151
152</div>
153<h2 id="Animation_Logic">Animation Logic</h2>
154<div id="Animation_Logic_CONTENT">
155<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>
156<p>Here's some initialization code to get started; put this below the code already supplied above:</p>
157<pre> use SDL;
158 use SDL::App;
159 use SDL::Rect;
160 use SDL::Color;
1f5d8082 161 use SDL::Video;
162a0989 162
163 # Create the SDL App
164 my $app = SDL::App-&gt;new( -width =&gt; $side_length, -height =&gt; $side_length,
165 -title =&gt; &quot;Simple Simulation!&quot;, -depth =&gt; 16, );
166
167 # white particles on a black background
1f5d8082 168 my $particle_color = SDL::Color-&gt;new( 0xff, 0xff, 0xff, );
169 my $bg_color = SDL::Color-&gt;new( 0x00, 0x00, 0x00 );
162a0989 170
171 # rectangles for the particles and the background
1f5d8082 172 my $particle = SDL::Rect-&gt;new( 0,0, 5, 5);
173 my $bg = SDL::Rect-&gt;new( 0,0,$side_length, $side_length, );
162a0989 174
175</pre>
176<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>
177<pre> # Run the simulation by (1) computing the updated positions, clearing the canvas, drawing the
178 # new particles, updating the visual display, and pausing before continuing:
179 for(my $t = 0; $t &lt; 20; $t += $d_t) {
180 compute();
181
182 # Clean the canvas
1f5d8082 183 SDL::Video::fill_rect($app, $bg, $bg_color);
162a0989 184 for(my $i = 0; $i &lt; $numb_of_atoms; $i++) {
185 $particle-&gt;x( $positions-&gt;at(0,$i) );
186 $particle-&gt;y( $positions-&gt;at(1,$i) );
1f5d8082 187 SDL::Video::fill_rect($app, $particle, $particle_color );
162a0989 188 }
1f5d8082 189 SDL::Video::flip($app);
162a0989 190 $app-&gt;delay(10);
191 }
192
193</pre>
194<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>
195
196</div>
197<h2 id="Disappearing_Particles_Some_of_the_p">Disappearing Particles!
198Some of the particles can drift off the screen. This is no good. What's causing this problem?</h2>
199<div id="Disappearing_Particles_Some_of_the_p-2">
200<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>
201<pre> sub compute {
202 $positions += $d_t * $velocities;
203
204 # Find all particles that are 'outside' the box, place them back in
205 # box, and reverse their directions
206 my ($bad_pos, $bad_vel)
207 = where($positions, $velocities, $positions &gt; $side_length);
208 $bad_vel *= -1;
209 $bad_pos .= 2 * $side_length - $bad_pos;
210 }
211
212</pre>
213<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>
214<pre> my $effective_length = $side_length - 5;
215 sub compute {
216 $positions += $d_t * $velocities;
217
218 # Find all particles that are 'outside' the box and push them back in the
219 # opposite direction, reversing their directions, too.
220 my ($bad_pos, $bad_vel)
221 = where($positions, $velocities, $positions &gt; $effective_length);
222 $bad_vel *= -1;
223 $bad_pos .= 2 * $effective_length - $bad_pos;
224 }
225
226</pre>
227<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>
228<pre> # Set up the system parameters, including random positions and velocities.
229 my $d_t = 2**-3;
230 my $side_length = 200;
231 my $particle_size = 5;
232 my $numb_of_atoms = 100;
233 my $positions = random(2, $numb_of_atoms) * $side_length;
234 my $velocities = grandom(2, $numb_of_atoms) * 5;
235
236</pre>
237
238</div>
239<h2 id="Disappearing_Particles_take_2">Disappearing Particles, take 2</h2>
240<div id="Disappearing_Particles_take_2_CONTEN">
241<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>
242<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>
243<pre> for(my $i = 0; $i &lt; $numb_of_atoms; $i++) {
244 $particle-&gt;x( $positions-&gt;at(0,$i) );
245 $particle-&gt;y( $positions-&gt;at(1,$i) );
1f5d8082 246 $particle-&gt;h( $particle_size );
247 $particle-&gt;w( $particle_size );
248 SDL::Video::fill_rect($app, $particle, $particle_color );
162a0989 249 }
250
251</pre>
252<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>
253<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>
254<pre> sub compute {
255 $positions += $d_t * $velocities;
256
257 # Find all particles that are 'outside' the box and push them back in the
258 # opposite direction, reversing their directions, too.
259 my ($bad_pos, $bad_vel)
260 = where($positions, $velocities, $positions &gt; $effective_length);
261 $bad_vel *= -1;
262 $bad_pos .= 2 * $effective_length - $bad_pos;
263
264 ($bad_pos, $bad_vel) = where($positions, $velocities, $positions &lt; 0);
265 $bad_vel *= -1;
266 $bad_pos *= -1;
267 }
268
269</pre>
270<p>You can also remove the explicit particle-sizing that we put in before, because it's no longer a problem.</p>
271<p>And there you have it! We have a fully fledged simulation of noninteracting particles in a box!</p>
272
273</div>
274<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>
275<div id="What_s_in_a_Name_Pesky_conflicts_wit-2">
276<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>
277<pre> Prototype mismatch: sub main::in (;@) vs none at ./sdlsandbox.pl line 36
278
279</pre>
280<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>
281<pre> use PDL qw( !in );
282
283</pre>
284<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>
285
286</div>
287<h2 id="Solution_1_Explicit_scoping_using_pa">Solution 1: Explicit scoping using packages</h2>
288<div id="Solution_1_Explicit_scoping_using_pa-2">
289<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>
290<pre> package MyCompute;
291 use PDL;
292 my $positions = random(2, $numb_of_atoms) * $side_length;
293
294 # ... and later
295 package main;
296 use SDL;
297
298 # ... and later, tweak the application loop
299 for(my $t = 0; $t &lt; 20; $t += $d_t) {
300 MyCompute::compute();
301
302</pre>
303<p>And now everything should run fine, without any more warnings!</p>
304
305</div>
306<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>
307<div id="Solution_2_Removing_SDL_s_in_or_PDL_-2">
308<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>
309<pre> # use SDL, but remove SDL's in function before loading PDL
310 use SDL;
311 BEGIN {
312 delete $main::{in};
313 }
314 use PDL;
315
316</pre>
317<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>
318<pre> # use PDL, but remove its 'in' function before loading SDL
319 use PDL;
320 BEGIN {
321 delete $main::{in};
322 }
323 use SDL;
324
325</pre>
326
327</div>
328<h1 id="Making_the_simulation_interactive">Making the simulation interactive</h1><p><a href="#TOP" class="toplink">Top</a></p>
329<div id="Making_the_simulation_interactive_CO">
330<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>
331
332</div>
333<h2 id="Present_state_of_the_code">Present state of the code</h2>
334<div id="Present_state_of_the_code_CONTENT">
335<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>
336<pre> #!/usr/bin/perl
337 # A simple simulation
338 use warnings;
339 use strict;
340
341 ## Global Variables ##
342
343 # Set up the system parameters, including random positions and velocities.
344 my $d_t = 2**-3;
345 my $side_length = 200;
346 my $particle_size = 5;
347 my $numb_of_atoms = 100;
348
349 ## Computational Stuff ##
350
351 package MyCompute;
352 use PDL;
353 my $positions = random(2, $numb_of_atoms) * $side_length;
354 my $velocities = grandom(2, $numb_of_atoms) * 6;
355 my $effective_length;
356
357 sub compute {
358 my $effective_length = $side_length - $particle_size;
359
360 # update the a real simulation, this is the interesting part
361 $positions += $d_t * $velocities;
362
363 # Check boundary conditions. Find all particles that are 'outside' the box,
364 # place them back in the box, and reverse their directions
365 my ($bad_pos, $bad_vel)
366 = where($positions, $velocities, $positions &gt; $effective_length);
367 $bad_vel *= -1;
368 $bad_pos .= 2 * $effective_length - $bad_pos;
369
370 ($bad_pos, $bad_vel) = where($positions, $velocities, $positions &lt; 0);
371 $bad_vel *= -1;
372 $bad_pos *= -1;
373 }
374
375
376
377
378 ## Animation Code ##
379
380 package main;
381
382 use SDL;
383 use SDL::App;
384 use SDL::Rect;
385 use SDL::Color;
1f5d8082 386 use SDL::Video;
162a0989 387
388 # Create the SDL App
389 my $app = SDL::App-&gt;new( -width =&gt; $side_length, -height =&gt; $side_length,
390 -title =&gt; &quot;Simple Simulation!&quot;, -depth =&gt; 16, );
391
392 # white particles on a black background
1f5d8082 393 my $particle_color = SDL::Color-&gt;new( 0xff, 0xff, 0xff, );
394 my $bg_color = SDL::Color-&gt;new( 0x00, 0x00, 0x00 );
162a0989 395
396 # rectangles for the particles and the background
1f5d8082 397 my $particle = SDL::Rect-&gt;new( 0,0, 5, 5);
398 my $bg = SDL::Rect-&gt;new( 0,0,$side_length, $side_length, );
162a0989 399
400 # Run the simulation
401 for(my $t = 0; $t &lt; 20; $t += $d_t) {
402 MyCompute::compute();
403
404 # Clean the canvas
1f5d8082 405 SDL::Video::fill_rect($app, $bg, $bg_color);
162a0989 406 for(my $i = 0; $i &lt; $numb_of_atoms; $i++) {
407 $particle-&gt;x( $positions-&gt;at(0,$i) );
408 $particle-&gt;y( $positions-&gt;at(1,$i) );
1f5d8082 409 SDL::Video::fill_rect($app, $particle, $particle_color );
162a0989 410 }
1f5d8082 411 SDL::Video::flip($app);
162a0989 412 $app-&gt;delay(10);
413 }
414
415</pre>
416<p>So there it is, top to bottom, in about 75 lines.</p>
417
418</div>
419<h2 id="Listening_to_Events">Listening to Events</h2>
420<div id="Listening_to_Events_CONTENT">
421<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>
1f5d8082 422<pre> use SDL::Event;
423 use SDL::Events;
162a0989 424
425</pre>
426<p>and then be sure to create an event object amongst the animation initialization code:</p>
f373167e 427<pre> my $event = SDL::Event-&gt;new;
162a0989 428
429</pre>
430<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>
431<pre> # Run the simulation
432 while(1) {
433 MyCompute::compute();
434
435 # Clean the canvas
1f5d8082 436 SDL::Video::fill_rect($app, $bg, $bg_color);
162a0989 437 for(my $i = 0; $i &lt; $numb_of_atoms; $i++) {
438 $particle-&gt;x( $positions-&gt;at(0,$i) );
439 $particle-&gt;y( $positions-&gt;at(1,$i) );
1f5d8082 440 SDL::Video::fill_rect($app, $particle, $particle_col10);
162a0989 441
1f5d8082 442 while(SDL::Events::poll_event($event) ) {
443 if($event-&gt;type() == SDL_QUIT) {
162a0989 444 exit;
445 }
446 }
447 }
448
449</pre>
450<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>
451
452</div>
453<h2 id="Responding_to_events">Responding to events</h2>
454<div id="Responding_to_events_CONTENT">
455<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>
456<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>
457<pre> # event dispatch table
458 my $keyname_dispatch_table = {
459 'up' =&gt; \&amp;incr_particle_size, # up key makes particles larger
460 'down' =&gt; \&amp;decr_particle_size, # down key makes particles smaller
461 'space' =&gt; sub { $d_t = -$d_t }, # space-bar reverses time
462 '.' =&gt; sub { $d_t *= 1.1 }, # right-angle-bracket fast-forwards
463 ',' =&gt; sub { $d_t /= 1.1 }, # left-angle-bracket slows down
464 'q' =&gt; sub { exit; }, # q exits
465 };
466
467 sub incr_particle_size {
468 $particle_size++;
1f5d8082 469 $particle-&gt;h($particle_size);
470 $particle-&gt;w($particle_size);
162a0989 471 }
472
473 sub decr_particle_size {
474 $particle_size-- if $particle_size &gt; 1;
1f5d8082 475 $particle-&gt;h($particle_size);
476 $particle-&gt;w($particle_size);
162a0989 477 }
478
479
480
481
482 # Run the simulation
483 while(1) {
484 MyCompute::compute();
485
486 # Clean the canvas
1f5d8082 487 SDL::Video::fill_rect($app, $bg, $bg_color);
162a0989 488 for(my $i = 0; $i &lt; $numb_of_atoms; $i++) {
489 $particle-&gt;x( $positions-&gt;at(0,$i) );
490 $particle-&gt;y( $positions-&gt;at(1,$i) );
1f5d8082 491 SDL::Video::fill_rect($app, $particle, $particle_color );
162a0989 492 }
1f5d8082 493 SDL::Video::flip($app);
162a0989 494 $app-&gt;delay(10);
495
1f5d8082 496 while(SDL::Events::poll_event($event) ) {
497 if($event-&gt;type() == SDL_QUIT) {
162a0989 498 exit;
1f5d8082 499 } elsif($event-&gt;type() == SDL_QUIT) {
500 if(exists $keyname_dispatch_table-&gt;{$event-&gt;keysym_name()}) {
501 $keyname_dispatch_table-&gt;{$event-&gt;keysym_name()}-&gt;();
162a0989 502 }
503 }
504 }
505 }
506
507</pre>
508<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>
509<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>
510
511</div>
512<h2 id="Final_State_of_the_Code">Final State of the Code</h2>
513<div id="Final_State_of_the_Code_CONTENT">
514<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>
515<pre> #!/usr/bin/perl
516 # A simple simulation
517 use warnings;
518 use strict;
519
520 ## Global Variables ##
521
522 # Set up the system parameters, including random positions and velocities.
523 my $d_t = 2**-3;
524 my $side_length = 200;
525 my $particle_size = 5;
526 my $numb_of_atoms = 100;
527
528 ## Computational Stuff ##
529
530 package MyCompute;
531 use PDL;
532 my $positions = random(2, $numb_of_atoms) * $side_length;
533 my $velocities = grandom(2, $numb_of_atoms) * 6;
534 my $effective_length;
535
536 sub compute {
537 my $effective_length = $side_length - $particle_size;
538
539 # update the positions. For a real simulation, this is the interesting part
540 $positions += $d_t * $velocities;
541
542 # Check boundary conditions. Find all particles that are 'outside' the box,
543 # place them back in the box, and reverse their directions
544 my ($bad_pos, $bad_vel)
545 = where($positions, $velocities, $positions &gt; $effective_length);
546 $bad_vel *= -1;
547 $bad_pos .= 2 * $effective_length - $bad_pos;
548
549 ($bad_pos, $bad_vel) = where($positions, $velocities, $positions &lt; 0);
550 $bad_vel *= -1;
551 $bad_pos *= -1;
552 }
553
554
555
556
557 ## Animation Code ##
558
559 package main;
560
561 use SDL;
562 use SDL::App;
563 use SDL::Rect;
564 use SDL::Color;
1f5d8082 565 use SDL::Video;
566 use SDL::Event;
567 use SDL::Events;
162a0989 568
569 # Create the SDL App
570 my $app = SDL::App-&gt;new( -width =&gt; $side_length, -height =&gt; $side_length,
571 -title =&gt; &quot;Simple Simulation!&quot;, -depth =&gt; 16, );
572
573 # white particles on a black background
1f5d8082 574 my $particle_color = SDL::Color-&gt;new( 0xff, 0xff, 0xff, );
575 my $bg_color = SDL::Color-&gt;new( 0x00, 0x00, 0x00 );
162a0989 576
577 # rectangles for the particles and the background
1f5d8082 578 my $particle = SDL::Rect-&gt;new( 0,0, 5, 5);
579 my $bg = SDL::Rect-&gt;new( 0,0,$side_length, $side_length, );
162a0989 580
581 # event listener
f373167e 582 my $event = SDL::Event-&gt;new();
162a0989 583
584 # event dispatch table
585 my $keyname_dispatch_table = {
586 'up' =&gt; \&amp;incr_particle_size, # up key makes particles large
587 'down' =&gt; \&amp;decr_particle_size, # up key makes particles large
588 'space' =&gt; sub { $d_t = -$d_t },
589 '.' =&gt; sub { $d_t *= 1.1 }, # right-arrow fast-forwards
590 ',' =&gt; sub { $d_t /= 1.1 }, # left-arrow slows down
591 'q' =&gt; sub { exit; }, # q exits
592 };
593
594 sub incr_particle_size {
595 $particle_size++;
1f5d8082 596 $particle-&gt;h($particle_size);
597 $particle-&gt;w($particle_size);
162a0989 598 }
599
600 sub decr_particle_size {
601 $particle_size-- if $particle_size &gt; 1;
1f5d8082 602 $particle-&gt;h($particle_size);
603 $particle-&gt;w($particle_size);
162a0989 604 }
605
606
607
608
609 # Run the simulation
610 while(1) {
611 MyCompute::compute();
612
613 # Clean the canvas
1f5d8082 614 SDL::Video::fill_rect($app, $bg, $bg_color);
162a0989 615 for(my $i = 0; $i &lt; $numb_of_atoms; $i++) {
616 $particle-&gt;x( $positions-&gt;at(0,$i) );
617 $particle-&gt;y( $positions-&gt;at(1,$i) );
1f5d8082 618 SDL::Video::fill_rect($app, $particle, $particle_color );
162a0989 619 }
1f5d8082 620 SDL::Video::flip($app);
162a0989 621 $app-&gt;delay(10);
622
1f5d8082 623 while(SDL::Events::poll_event($event) ) {
624 if($event-&gt;type() == SDL_QUIT) {
162a0989 625 exit;
1f5d8082 626 } elsif($event-&gt;type() == SDL_QUIT) {
627 if(exists $keyname_dispatch_table-&gt;{$event-&gt;keysym_name()}) {
628 $keyname_dispatch_table-&gt;{$event-&gt;keysym_name()}-&gt;();
162a0989 629 }
630 }
631 }
632 }
633
634</pre>
635<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>
636
637</div>
638<h1 id="Directions_for_future_work">Directions for future work</h1><p><a href="#TOP" class="toplink">Top</a></p>
639<div id="Directions_for_future_work_CONTENT">
640<p>I have a couple of ideas for future work combining PDL and SDL.</p>
641<dl>
642 <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.
643=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>
644</dl>
645
646</div>
647</div>