Functions#
What if I told you, you could define your own commands like circle()
and line()
? All of these things we called "commands" until now are actually nothing special, they are what we call Functions.
You have also already created your own functions with void setup() { ... }
and void draw() { ... }
, but these are special in Processing, so we didn't go into much detail then. We will go into all these details right now!
What are Functions?#
Functions are reusable pieces of code that you give your own name. You then get to run that name like your own command. You are calling your function. Let's have an example! In a previous session, we created this neat little tree:
translate(20, 20);
// trunk
fill(50, 50, 0);
strokeWeight(0);
rect(-3, 20, 6, 10);
// top part
fill(0, 100, 0);
beginShape();
vertex(0, 0);
vertex(10, 10);
vertex(5, 10);
vertex(15, 20);
vertex(-15, 20);
vertex(-5, 10);
vertex(-10, 10);
endShape(CLOSE);
// ornament
fill(255, 0, 0);
circle(0, 0, 5);
pushStyle();
stroke(255, 255, 255);
point(-1.5, -1.5);
popStyle();
circle(5, 3, 3);
nothing special here, just a few drawing commands. What if we wanted to draw a whole little forest? That would just be tedious, right? Wrong! Let's make our own little pine tree function that we can call as often as we want:
Whoa, what just happened? We added just a few lines of code, but now we magically get three trees instead of one? First of all, we surround our previous code in a block of code with {
and }
in lines 1 and 27. All of this is also indented by a little bit to make it easier to recognize what's "in" this function. All of these 25 lines are now within our function block. We then give our function two things: a return type (void
) and a name (pineTree()
). We will talk about return types later. For now we are not using it. This is our first function!1
All we need to do now, is to just call our functions, in this example we do this in lines 31, 33 and 35 by writing pineTree()
, each time we move our drawing a little bit to the side with translate()
in between. We made our pineTree()
reusable.
Names are Important!
You have heard me ramble about the importance of good names often enough. All of this rambling applies to function names as well, of course! A function name should express what the function does in a succinct way.
In Java, the convention is to use lower case camelCase
like pineTree()
or endShape()
. If you go on to write other languages, those conventions will differ (python and ruby use lower case snake_case
; C# uses upper case CamelCase
).
Parameters#
We've talked about parameters before, those were numbers we could give to commands to draw our circle somewhere else. But as we just saw, that was not a command, that was a function all along. So can we have our own parameters, pretty please?
void pineTree(int x, int y) {
push();
translate(x, y);
// trunk
fill(50, 50, 0);
strokeWeight(0);
rect(-3, 20, 6, 10);
// top part
fill(0, 100, 0);
beginShape();
vertex(0, 0);
vertex(10, 10);
vertex(5, 10);
vertex(15, 20);
vertex(-15, 20);
vertex(-5, 10);
vertex(-10, 10);
endShape(CLOSE);
// ornament
fill(255, 0, 0);
circle(0, 0, 5);
pushStyle();
stroke(255, 255, 255);
point(-1.5, -1.5);
popStyle();
circle(5, 3, 3);
pop();
}
void draw() {
pineTree(20, 20);
pineTree(35, 20);
pineTree(50, 20);
}
In line 1, we not only give our function a return type (still void
, still to be explained later), a name pineTree
, but in its parentheses, we now have something that looks like we're defining variables with int x, int y
- and we totally are! This tiny addition means "our own pineTree
-function takes two parameters. The first one should be an int
and we will be calling it x
within our function. The second one will also be an int
, and within the function that will be called y
.
Later on, in line 3, we use this x
and y
to do a translate()
of our pine tree in the pineTree()
function itself. That means that we can now call pineTree(20, 20);
and draw it in some place like we did with circle(20, 20)
all along!
Parameters can be of any type you need, they could also be String
or boolean
or types you will later get to define yourself (ominous foreshadowing).
Functions in Functions - and the Call Stack#
Our tree needs more ornaments, that's for sure! With functions making stuff more reusable, it would be amazing if we could use functions for this.
So, in this example, draw()
calls pineTree()
twice, each time with its own coordinates. And within pineTree()
, it calls ornament()
five times, each time with different coordinates and different colors!
At the same time, our code got a whole lot more readable, because all those circles and strokes now have a human-readable name. It is more modular than it was before.
But more importantly this is a very simple example of a function calling another function. This is also something you have been doing all along, when you were calling circle()
and similar functions from within your draw()
function and so on. This list of functions calling functions calling functions … is called a call stack because at some point while your program runs, this will be true:
- Something from within Processing will call your
draw()
function. - In your Your
draw()
-function in line46
, it will callpineTree()
. pineTree()
line38
will callornament()
.ornament()
line6
will callcircle()
.circle()
line3065
, which is not our code, will itself call theellipse()
-function!ellipse()
itself also does a few things and then calls another function and so on.
In many languages, you will see a list like the above when an error happens. In those cases it will try to help you pin down the error by showing you the call stack (or stack trace in the moment that error happened.) Here is the stack trace I just described:
java.lang.Exception
at ornament(example.java:6)
at pineTree(example.java:38)
at draw(example.java:46)
at processing.core.PApplet.handleDraw(PApplet.java:2094)
at processing.awt.PSurfaceAWT$9.callDraw(PSurfaceAWT.java:1386)
at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:356)
All the way at the top is our own ornament
function, which sits in example.java
in line 6 (:6
means line 6). We then see pineTree
(same file), draw
(same file) and their respective line numbers. After that we go into some Processing internals that we've never seen before (and likely won't be seeing again in this course!). handleDraw()
is a function in PApplet.java
, and is in line 2094. This itself is called by another function and so on.
Ever been to StackOverflow?
StackOverflow has its name directly from this little thing. A stack overflow is a certain kind of error where this call stack gets so huge that your computer can't deal with it any more. That whole name is a huge in-joke for programmers.
Assignment: Use Functions in your existing assignments!
With this new found knowledge, make changes to your previous assignments! Take your portrait, and put individual parts of it in separate functions (eye, ear, hair ... use your best judgement!). Do the same for your halloween decoration (pumpkin, bat, spider ... whatever you have drawn)
Use this opportunity to also practice the use of your own parameters.
Commit and push the results to Gitlab by 2023-11-22 18:30.
Relevant excerpt from Learning Processing#
(the section starts at 3:40:29 and runs through 3:57:00, the video should start and stop on these automatically.)
-
It is not. You were defining your own draw and setup functions for a few weeks, now. I just didn't tell you. ↩