Design Computation

Functions

Structuring code is important, especially with regards to your thinking.

So far we’ve redefined functions built into Processing: setup(), draw(), mousePressed(), etc., which are event-driven. They execute when the user does something, or “triggers” the event. These functions handle occurrences that are asynchronous. That is, they don’t happen predictably on the computer’s internal clock cycle.

Customizing Functions

Functions (also called methods) are a structural method of organizing code that is executed as a set.

In general, a function, or sometimes “subroutine”, is a set of instructions defined under a single name. Functions are intended to be executed many times under different conditions. They are used for organization, clarity of expression, and convenience, i.e. you don’t have to write all the instructions over and over when you need them.

Analogy

Functions are analogous to typed commands in AutoCAD or Rhino, and functions in mathematics, where numbers go in and numbers come out (e.g. y=f(x) ).

setup() and draw()

We have actually already been defining functions the entire time. setup() and draw() are special functions that are empty by default. In order to write a sketch, we have to give those functions a set of instructions to refer to, between the braces: { … }.

Functions as Types

In the computational world, a “function” is just another type of data, but its contents aren’t an integer or floating point number, but a set of instructions. Some languages allow us to use functions in even more flexible ways.

When using our own customized functions, we have to go through a similar process as with variables to declare and initialize them, although we use different terminology. Instead of declaring, initializing, and assigning, we “define” functions and “call” them.

But it’s still about giving something a name so we can control it.

Defining Functions


void setup(){
  size(700,400);
}

void draw(){
  background( 255 );
  drawCircleInSquare();
}

void drawCircleInSquare(){
  rect(mouseX-25,mouseY-25,50,50);
  ellipse(mouseX,mouseY,50,50);
}

Here, we “define” a function by using a familiar syntax, just like setup() and draw().

In this case, we have defined a function called drawCircleInSquare, which contains two lines of code. It simply does what the name implies, drawing a circle centered on a square, centered on the mouse.

We “call” our function like calling any other function in Processing, by writing the name, a set of parentheses, and a semicolon.

Functions as Control Mechanisms

Again, using a function is about setting aside a set of instructions under a single name. But once we start considering how Processing executes the code, we can see that when Processing reaches the line where we ask it to call our function, Processing “jumps” to the definition of our function’s code and starts executing the lines inside that code block.

When Processing reaches the end of the function, it returns to where the function was originally called, and continues along its way.

Adding Parameters (Arguments) to Functions

The syntax for declaring a function with parameters follows the same notations as in the API.

Functions are parameterizable just as those that come with Processing.

Functions like line() accept parameters (also called “arguments”). You have to tell the line() function where the endpoints of the line are in terms of coordinate values. Technically speaking, you “passed” the function four values.

If you want to pass parameters to your own functions, you have to define their types and the names of the variables to use to represent those values in the interior of the function.


void setup(){
  size(700, 400);
  noStroke();
  fill(0);
}

void draw(){
  background(255);
  drawPulsatingCircle( 100, 100 );
  drawPulsatingCircle( 500, 200 );  // Not drawn.
  drawPulsatingCircle( mouseX, mouseY );
}

void drawPulsatingCircle( float x, float y ){
  float radius = sin( radians(frameCount) )*100;
  
  // x, y, and radius are floats that are
  // only valid inside this code block.

  ellipse( x, y, radius, radius );
}

// x, y, and radius are not "defined" here.

Returning a Function Early

The keyword “return” aborts a function early, and “returns” to the place where it was called.


void setup(){
  size(700, 400);
  noStroke();
  fill(0);
}

void draw(){
  background(255);
  drawPulsatingCircle( 100, 100 );
  drawPulsatingCircle( 500, 200 );  // Not drawn.
  drawPulsatingCircle( mouseX, mouseY );
}

void drawPulsatingCircle( float x, float y ){
  // x, y, and radius are floats that are
  // only valid inside this code block.

  float radius = sin( radians(frameCount) )*100;
  
  /* 
    Doesn't draw a circle if it's on the right
    side of the sketch. Just demonstrates how
    to use 'return' inside a function.
  */
  if( x > width/2 ) return;
  
  ellipse( x, y, radius, radius );
}

// x, y, and radius have no meaning here.

Functions That Return Values

You’re just seeing the internal mechanism behind Processing’s own functions.

Functions can perform any set of operations, calculating, drawing, etc.

Functions can also manipulate data. These functions are like math functions, but you don’t have do deal just with numbers, but all kinds of data (Strings, objects, etc.).

Returning a value is just a matter of using the “return” keyword. Giving it a value or variable returns that variable to the calling function.

‘void’ means the function doesn’t return a value, i.e. it doesn’t compute something and then give you the answer. It just does something.


int x = 100;

void setup(){
  size(700,400);
}

void draw(){
  background(255);
  
  stroke(0);
  line( mouseX, 0, mouseX, height );
  ellipse( mouseX, mouseY, 10, 10 );
  
  float avg = average( x, mouseX );
  
  line( x, 0, x, height );
  
  stroke(255,0,0);
  line( avg, 0, avg, height );
}

float average( float a, float b ){
  return (a+b)/2.0;
}

void mousePressed(){
  x = mouseX;
}

Note on Variable Scope

Variables are only good within the code block in which they are defined. In Processing, so-called “global” variables are those declared outside setup(), draw(), or any other function. They are available and valid variables for any function, in any code block, etc.

But those variables declared as function parameters or within functions are only valid in that code block and all contained code blocks. This is their “scope.”


float globalVariable = 10;

void setup(){
  size(700,400);
  int variableValidInSetupOnly = 5;
  println( variableValidInSetupOnly + globalVariable );
}

void draw(){
  background( 255 );
  globalVariable = myFunc( mouseX, mouseY );
  println( globalVariable );
}

float myFunc( int x, int y ){
  float sum = x + y;
  sum += globalVariable;
  return sum;
}

// sum is not valid here.