"Functions break large
computing tasks into smaller ones, and enable people to build on what others
have done instead of starting over from scratch. Appropriate functions hide
details of operation from parts of the program that don't need to know about
them, thus clarifying the whole, and easing the pain of making changes."
-- Kernigen & Ritchie
In processing, we've been using functions all along. When we say
"line(0,0,200,200)" we are calling the function "line", a
"built-in" function of the processing environment. When we say
"void setup()", we are declaring the "setup" function,
which is a special processing function that is called when the program first
starts.
A function
declaration/definition has three parts:
· return type
· function name
· parameters
It looks like this:
ReturnType FunctionName ( Parameters ) {
code body of function }
Here's a simple example:
void sayingHello() { println("I'm saying hello!")}
This is a simple function
that performs one basic task -- printing the phrase "I'm saying
hello!"). It has no return type, and therefore we say "void". It
has a function name: sayingHello. It has no parameters (and therefore nothing
is placed in between the parentheses). We can call the function (and therefore have
the instructions of the function executed) like this:
sayingHello();
Things
to remember
· The value you
pass as an argument to a function can be a literal value (20,5,4.3f, etc.) or a
variable of the declared parameter type.
· A function must
always have a return type. If it doesn't return anything, you should declare
the return type as "void"
· If the return type is not void, your function must return a value compatible with the declared return type
Ok, now let's look at an
example that makes use of a return type and parameters.
int add(int a, int b) { int sum; sum = a + b; return sum;}
In the first line above, we
first have "int" -- which declares the "return" type of the
function. Second, we have the word "add" -- this is our name for the
function; much like declaring a variable name, we can use any word we want,
staying away from already defined keywords (such as primitive data types,
built-in processing function names, etc.) Finally, we have some stuff in
between paratheses. These are called "parameters" and we can have as
many of them as we like.
So, what are all these things doing? Examine the following code:
int x = 5;
int y = 7;
int total = 0;
total = add(x,y);
println(total);
In
the above code, the function "add" is being called, with two
parameters (x and y) and the result of that function is then assigned to the
variable "total."
· Return -- The function
add has a return type "int." This means that at the end of the
function, we will have a statement "return x" where x is some integer
value to be sent back to the calling area. This resulting value can be assigned
to a variable of type "int" (in this case "total") or used
in an expression, i.e. line(0,0,200,add(100,50)).
· Parameter Passing -- x
and y, which hold the values 5 and 7, respectively are "passed" into
the function "add." They are called the "arguments."
Arguments that are passed to a function arrive in the same order as they were
passed. The first argument lands in the first parameter, second in the second,
and so on. Variables passed as arguments must have the same type as the
parameters in the function definition.
· Local Variables -- it
should be noted that the above function "add" declares a local
variable called "sum" which is uses to compute the value of a plus b.
Functions can have local variables, and these variables are allocated and
initalized when a function is invoked. When the function returns, they are then
destroyed. The parameters also act as local variables -- but how does this
work?
In Java, when talking about
functions, all primitive variables are "passed by value." What does
this mean?
Assume the following:
int x = 7;
A variable, x, is declared
and assigned the value '7'. In other words, the bit value for 7 is stored
*inside* the variable x. Now, let's say we have declare function called
"blah", with an int parameter named y:
void blah (int y) { //some code}
When we call the function blah,
passing the variable as the argument. . .
blah(x);
. . .the bits in x are
copied, and the copy lands in a new storage location, that of y. What's crucial
to note is that x and y are not linked in any way -- the copy is a one-time
operation. If the value of y is changed during the the function
"blah", the value of x in the calling area remains the same. Run the
following code and see what happens:
int x = 0;
void setup() {println("When the program starts, the value of x is:" + x);
change(x); println("A copy of x was passed to the function so the value of x remains: " + x);}
void change(int y) { println("Inside the function, y receives the value of x and is: " + y); y = 5; println("Inside the function, y is assigned the value: " + y);}
What if x, however, were an
array? In this case, java passes the parameter by value, but instead of
passing a copy of the data inside a variable, it passes a copy of the array's
reference. And so in this case, the values of the elements of the array can be
changed. Run the following code, try to follow the logic, and see the
difference:
int[] x = {0,0};void setup() {println("When the program starts, the reference address for the array x is: " + x);
for (int i = 0; i < x.length; i++) {println("When the program starts, the value of element # " + i + " of the array x is: " + x[i]);
} change(x);println("After the function is called, the reference address for the array x remains: " + x);
for (int i = 0; i < x.length; i++) { println("However, the values of x have been changed, element # " + i + ": " + x[i]); }}
void change(int[] y) { println("Inside the function, y receives the reference address to the array x: " + y); for (int i = 0; i < y.length; i++) { println("Y has the same values as x, element #" + i + ": " + y[i]); y[i] = 5; println("The value of element #" + i + " of y is changed to: " + y[i]); }}
Now that we've learned all
about functions, let's take one of our previous examples and organize it with
functions. Having our code well-organized will also allow us to more easily
re-use and add features.
//a variable to store the maximum # of elements in the array
int MAX = 250;
//declare array names and allocate the space to store them
float[] x = new float[MAX];
float[] y = new float[MAX];
float[] yspeed = new float[MAX];
float[] xspeed = new float[MAX];
color[] c = new color[MAX];
void setup() { size(400,200); colorMode(RGB,255,255,255,100); initArray(x,0,width); initArray(y,0,height); initArray(xspeed,-3,3); initArray(yspeed,-3,3); fillColors();}
void loop() { //set background stroke and fill moveStuff(x,xspeed); moveStuff(y,yspeed); boundary(x,-20,width+20); boundary(y,-20,height+20); drawStuff();}
//fill array with random values
void initArray(float[] a, int min, int max) { for (int i = 0; i < a.length; i++) { a[i] = random(min,max); }}
//fill color array
void fillColors() { for (int i = 0; i < MAX; i++) { c[i] = color(200, i % 255,0,(i) % 100); }}
//adjust values of one array by another
void moveStuff(float[] a, float[] speed) { for (int i = 0; i < a.length; i++) { a[i] += speed[i]; }}
//check to see if element reaches boundary
void boundary(float[] a, int min, int max) { for (int i = 0; i < a.length; i++) { if (a[i] > max) { a[i] = min; } if (a[i] < min) { a[i] = max; } }}
void drawStuff() { //background(0); fill(0,0,0,30); rect(0,0,width,height); noStroke(); smooth(); for (int i = 0; i < MAX; i++) { fill(c[i]); ellipse(x[i],y[i],i/10,i/10); }}
Draw Targets
Rollover
Functions
Button
Interpolate
In processing, we can store
an image in a variable of type BImage. This data type will also allow us access
to the pixels of the image as well as the width and height of the image.
Following is how we initialize an image. To display an image, we call the
"image" command, followed by our image variable, and the coordinates
for displaying the image. We can also adjust the width and height of the image,
using two more parameters.
BImage b;
b = loadImage("filename.jpg");image(b, 0, 0); // w/ default width and heightimage(b, 0, 0, 100, 50); // w/ height and width defined
Finally, we can tint the
image with an RGB color, as well as give it an "alpha" value. To do
so, we call the tint function with 4 parameters, R,G,B, and alpha. See an
example below. Note that white (255,255,255) gives the image no tint, black
(0,0,0) makes the image invisible.
BImage a,b;
void setup() { size(200,200); a = loadImage("kerry.jpg"); b = loadImage("bush.jpg");}
void loop() { colorMode(RGB,255,255,255,width); tint(255,255,255,width-mouseX); image(a, 0, 0); tint(255,255,255,mouseX); image(b,0,0);}
. . . and here it is w/
rotation, just 'cause we can. . .
BImage a,b;
float x;
void setup() { size(200,200); x = 0.0f; a = loadImage("kerry.jpg"); b = loadImage("bush.jpg");}
void loop() { background(0); translate(width/2,height/2); imageMode(CENTER_DIAMETER); colorMode(RGB,255,height,height,width); rotate(x); x += 0.5f - float(mouseX) / float(width); tint(255,height-mouseY,mouseY,width-mouseX); image(a, 0, 0); tint(255,mouseY,height-mouseY,mouseX); image(b,0,0);}
display image
image
transparency
background
image
back to syllabus