What are setup() and draw()?

edited November 2014 in Common Questions

What are setup() and draw()?

OK, I cheat, this isn't really a frequently asked question, but I needed a common section for these two questions.

And it will allow to correct some frequently made errors...

First notice: in these answers, I will be imperative, giving some "rules". These are actually guidelines, showing the most frequently used practices, found in a vast majority of sketches. These rules are not rigid, you can find ways to bend or break them, or just to do things differently. Once you master the bases and know what you are doing... :-)

setup() and draw() are special functions, in the sense you will never call them. You create them and Processing will call them automatically.

When you run a sketch, a number of steps are processed. First, the global variables are initialized with the given values, or default values (0, null, false, etc.) if none are given.

Then setup() is called, only once per sketch run. It can be used to define the base sketch setup, like its size, its mode (default, OpenGL, etc.), for loading resources, etc.

draw() is called on each frame: a sketch is like a movie, it is made of successive frames, images, and their quick succession (by default around 60 per second) makes the eye believe there is a continuous movement.

There are a number of beginner's errors I see regularly, I will try and address them here. The following code show what must not be done!

PImage img1 = loadImage("A.Png");
PImage img3;
PImage img4;
PImage img5;
int halfWidth = width / 2;

void setup()
{
  img5 = loadImage("E.png");
  size(500, 500);
  PImage img2 = loadImage("B.png");
  PImage img3 = loadImage("C.png");
}

void draw()
{
  PImage img4 = loadImage("D.png");

  image(img1, halfWidth, 0);
  image(img2, 100, 0);
  image(img3, 200, 0);
  image(img4, 300, 0);
  image(img5, 400, 0);
}

Common errors

img1 won't work: the loadImage() part is at the level of the global declarations. These variables, generally defined above setup(), are said to be global because they are accessible from the whole sketch, in any function or class. Java (thus, Processing) will execute such code before everything else, including the call to setup().
But, before setup() is called, Processing hasn't computed yet some important data, like width and height (computed after the size() call), or the path to the sketch, thus the path to the data folder. Since, by default, the sketch takes images from this folder, the call will fail.

So, beginners just move the declaration inside setup, like done with img2. But they get an error when they use it in draw(): the life of a (local) variable declared inside a function is limited to the function itself -- that's the concept of scope, the variable is not available before it is declared and becomes unavailable outside of the enclosing braces {}. It is true for any pair of braces (ie. within a if or a for too, for example).

A common variant of the previous error is to keep the global declaration (so that it is reachable from all functions) and to paste the old declaration in setup(), like done with img3. But then we get a NullPointerException (NPE) when using it in draw(). Why? Because img3 is declared again (with its type) in setup(), and shadows (masks/hides) temporarily the global variable: ie. setup(), from this declaration to its end, only knows the internal variable. And then, the external/global img3 remains uninitialized. Thus the NPE, occurring when we try to use an uninitialized object variable (thus having a value of null).

img4's load is yet another way to try the loading, and somehow it can work, if you use it only in draw(). But then people comes to the forum, complaining their sketch is very slow... Indeed, doing unconditionally a loadXxx() call in draw (where Xxx can stand for Image, Shape, Strings, etc.) is a bad idea, because it means Processing will do a file access (and reading) some 60 times per second (if keeping the original frame rate)! In general, it is more efficient to read such file in setup() and to keep the read data at global level.

img5's load is nearly correct, but it has a drawback: it doesn't respect the rule saying that the size() call must be the first one in setup(). For some reason, code before it tends to be run twice, which is annoying when loading a possibly large file.

I added a halfWidth variable initialized at the global level. It will be actually zero. Why? Because when it is initialized, setup() hasn't been called yet, so size() hasn't be called either, so the variable width has its default value, zero.

So, what is the correct way to do that? Something like the following code:

// Declaration of the variable
PImage img;
int halfWidth;

void setup()
{
  size(500, 500);
  // Initialization of the content of the variable
  img = loadImage("A.png");
  halfWidth = width / 2;
}

void draw()
{
  // Using the global variable
  image(img, halfWidth, 0);
}
Tagged:
Sign In or Register to comment.