registerDraw()

Douglas Edric Stanley

2007.12.28

If you look inside the Processing code, you’ll find a helpful little service that isn’t mentioned in the documentation: registerDraw(java.lang.Object o) & unregisterDraw(java.lang.Object o).

These two functions are particularly helpful if you’re working with multiple objects. For example, imagine you had a class for a bunch of circles dancing around the screen; i.e. something like this (Yes, I know, this code is lame; just bear with me.):

////////////////

class Circle {

  float x, y;

  Circle() {
    x = random(width);
    y = random(height);
  }

  void draw() {
    x = ((x + random(-1,1)) + width) % width;
    y = ((y + random(-1,1)) + height) % height;
    ellipse(x,y,11,11);
  }

}

////////////////

You can then make twenty of these circles in your setup() as so :

////////////////

Circle[] circles = new Circle[20];

void setup() {
  size(256,256);
  for(int i=0; i<circles.length; ++i) circles[i] = new Circle();
}

////////////////

Now all you have to do is to call each and every one of those 20 circles in your draw() function through a for() loop :

////////////////

void draw() {
  background(255);
  for(int i=0; i<circles.length; ++i) circles[i].draw();
}

////////////////

But there has to be a cleaner way, right? You already wrote void draw() inside of your class, why not just let Processing do all the work of calling each object’s draw() for you?

Here is a complete re-write of the above program, which makes it all a bit cleaner:

////////////////

Circle[] circles = new Circle[20];

void setup() {
  size(256,256);
  for(int i=0; i<circles.length; ++i) circles[i] = new Circle();
}

void draw() {

}

public class Circle {

  float x, y;

  Circle() {
    x = random(width);
    y = random(height);
    registerDraw(this);
  }

  void draw() {
    x = ((x + random(-1,1)) + width) % width;
    y = ((y + random(-1,1)) + height) % height;
    ellipse(x,y,11,11);
  }

}

////////////////
abstractmachine registerDraw Processing example

Note how I have left the void draw() { } function completely empty. This is to show that the objects are all drawing themselves automatically. Why draw() at all, then? Well, you have to be careful here, because draw() is a required function, otherwise the Processing core will not animate your circles. There are other ways around this, but I usually leave the draw() in there, because it allows you to add a background(255), for example, to clear out the Sketch before drawing into it.

Also note, that the classes have to be declared public. I’ll explain more on this in my upcoming courses on objects and good practices. But for a short answer as to why the public keyword: your Sketch (called an applet) has ownership of all the objects declared inside of it. These are all private, i.e. only your own Sketch can call its own functions. In order for Java to automatically call all of your draw() functions within the objects, you have to declare them accessible (public) to the Processing core which does not have ownership of those objects. If all that sounds complicated (and even worse, I might have gotten some of that wrong — comments are welcome!), just forget it.

One eventually complicated part is determining in which order you will have the various objects drawn. This is very easy to control. You just registerDraw() in the order you want them to be drawn. Easy, non? And if you want to change their order? That’s easy too: just call unregisterDraw(); and re-register it. unregisterDraw() requires the address to the object, so you probably write something like this: unregisterDraw( circles[0] ); which would unregister the first circle. Again, if you don’t understand what I’m talking about, you probably should just ignore all this.

I suppose the most complicated part for beginners is the idea of this. I don’t really have the time to go into « this » right now. But again, for a quick explanation… this is a pointer to the object that contains the draw() routine you want auto-executed. Whenever you read this, the computer instead reads something else entirely: a specific memory address. Why? For simplicity (from the computer’s perspective at least) and control. Each object reads the same code from the class Circle, but each object has its own memory space containing its own variables for that code, as well as instructions on what it is supposed to do with those variables (defined by your class). You control these different objects by talking to them wherever they reside in your computer’s memory. The word « this » points to the address of that unique object. This address is what you need to give to registerDraw() and unregisterDraw() in order for the computer to know what to register or unregister.

Although I should point out that I have performed no speed tests on this method, I suspect it is nearly identical to calling each object yourself inside of your own draw() function. Looking in the Processing core would probably give me the answer, but I’m too lazy: hey, it’s the holidays. So why do I say it’s cleaner? Well, I like to use Processing’s tabs to hide away as much of the code complexity as I can in the far depths of the various classes. I’ve even started making classes inside of classes (ex: recent (tutorial:Wiimote link:happy-code-farm/processing-wiimote-balance-board) class made during the Geneva workshop), so I don’t really want to be bothered with all that complexity once I’ve gotten it right. Processing is all about making code as simple as possible, non ?

Original Comments:

2007-12-28 16:09:02

lenny

ha!
this is great. thank you.
i’ll go and kill all them loops !!!

2007-12-28 17:50:21

lenny

yesss,works like a charm.
thank you so much. :)

2007-12-28 18:12:49

Douglas Edric Stanley

:-)

2007-12-28 20:37:39

chandler

There is an entry on this at processing hacks as well. Makes life easier for sure.

2007-12-28 20:44:34

Douglas Edric Stanley

Oh. Cool. Thanks, I just read it. It shows how to catch mouse events too. I should have added that, duh. I always forget to look at Processing Hacks (and participate).