…in which we expand from a point.
In this final instalment of the particle system post series, we’ll expand the emitter so that we can map it onto some object in our scene. I say “final” instalment, but there might be one more in which I clean up the code and maybe show off some effects, although the GIFs I’m able to upload here have to be so small that that hardly seems worth it. You can come up with your own examples, of course, and that’s the point.
We’d like to emit particles from some larger area than a single point. But what kind of area? We could define special emitters for various geometrical shapes, but perhaps the nicest option is to allow any shape whatsoever, and even allow the shape to be animated over time. This might not be possible, but let’s try it.
We’ll use a PGraphics object, which is similar to a PImage except we can draw on it directly in Processing, using all its usual commands just as if we were drawing on the screen.
The idea is to create a PGraphics that has only black and white pixels. Black pixels are the emitter; white ones aren’t. We place the PGraphics over the central point of the ParticleSystem. When a new particle is born, we pick a random pixel from the PGraphics, trying again until we get a black one.
This “trying again” is the part that might give us trouble. It will work OK if most of the pixels are black, since the chances are we’ll find one pretty quickly. If only a few are black, giving birth to a new particle will take many attempts and the whole thing will slow down.
Let’s add a PGraphics to ParticleSystem and pass it in through the constructor in the usual way:
Now let’s fix the sketch by creating a PGraphics and passing it into the constructor. I’m going to make it a global variable so we can draw on it in the draw() function too:
Notice three changes here — lines 2, 7 and 13 in the screenshot above.
For testing purposes, let’s just draw something simple on the emitter (we can get more fancy with this later). Notice that you need to use beginDraw() and endDraw(), but besides that drawing on emitter is more or less the same as drawing on the screen:
Right, we have a circular emitter and it’s stored in ParticleSystem. Let’s now make sure all particles are born inside the emitter. We’ll do this in two steps — first we’ll just look for one black pixel in the emitter:
There might be some unfamiliar things here. First, the pixels in a PIimage, a PGraphics or on the screen are represented internally as a 1-dimensional array. We just want any pixel, so we don’t need to convert to x and y coordinates yet — we can just pick any random pixel from the array.
Let’s try to express it in words. Lines 79 and 80 start us off with the pixel in position 0 (because we have to start somewhere) and record that we have not yet found a black pixel. The while loop runs the code in its curly brackets over and over again until its condition is false: in this case, until !gotPixel — pronounced “not got pixel” — is false. That is, we try until the boolean variable gotPixel is true.
Inside the curlies we pick any random pixel (line 83) and then check whether it’s black (line 84 — using the brightness channel is a lazy way to do this). If it is indeed black then we set gotPixel to true, which will cause the while loop to stop repeating.
Next we need to say what this means for the position of the particle. My sense is that we should think of the emitter as being centred on the “centre” of the ParticleSystem, so we need to do a bit of pixel arithmetic to figure out where our new particle should be born on the screen — study this carefully if you’ve never done this kind of thing before, you will surely have to do it many times in the future:
Here pixelX is obtained by taking the modulus of the emitter’s width. What that means is that we “factor out” full widths of pixels and just look at what’s left over. On the other hand, pixelY is obtained using integer division, which only counts full-image-widths of pixels and throws away the remainder. These two tricks work because of how the 2D pixel grid is represented by the 1D array. It could have been represented in some other way and that would mean a different set of tricks would be needed, so don’t feel bad if you don’t find this obvious.
This code works but it isn’t very obvious. Let’s animate the emitter! I made it 200×200 pixels and gave it a simple rotating rectangle:
To check what your emitter looks like, simply show it in draw() and comment out the ParticleSystem stuff (to get this to work you’ll also need to comment out the ParticleSystem’s constructor in setup, for reasons we’ll get to in a second):
Here’s what our emitter looks like:
And now for the moment of truth — uncomment the particle system, comment out printing the emitter and see what happens. The result is not good — it crashes. Why? Because when you create the ParticleSystem in setup(), the emitter hasn’t been drawn on yet, so its pixels array isn’t set up. But it tries to initialize all the particles at that moment — they try to look at the pixels of the emitter and boom, your sketch blows up.
To fix this we just need to draw the first frame on the emitter in setup(). The easy way to do this is to take the “draw on the emitter” code out into a function and call it in setup(), then call it in draw() as well. That’s much better than having duplicated code. Here’s the function, taking in an int representing the frameCount:
Here’s how we call it in setup():
and something very similar in draw(), replace the old emitter-drawing code of course:
Now we get the result we wanted — particles being emitted from a rotating rectangle:
With this, I think we’re pretty much done with the particle system. There’s one small thing I wanted to add that I’d forgotten about until now and that’s control over the particles’ speeds. While we could have a startSpeed and endSpeed and lerp between them, let’s keep things simple and add a float called speed to ParticleSystem, multiplying mov by it in init() to give the particles the desired speed. I’ll leave that as an exercise for you.
As ever the code, including the addition of the speed variable, is on GitHub for your enjoyment. I’ve slightly tidied up the code for this release but some areas — especially the ParticleSystem class — are still much too messy and hard to work with. I’ll probably do a bit more of this but I doubt it will be worth a post.
I’m sure you can think of many extensions and enhancements for this system — let me know if you get somethinginteresting working!
If you’re fairly new to Processing, this series may have gone too fast. I suggest reading through it again from the start — it may make more sense now you know where we’re going. Once you feel confident, don’t just clone my code: try building your own particle system using some of the principles you’ve picked up along the way.