An Introduction To Programming With Processing

Interface Controls: Setting up a Slider

In the following sketch use what we have learned to create a slider that can return a numerical value, which we will map to the tinting of an image. The best place to start with a program is always doing what your are already most familiar with so lets start by loading all the images into the sketch:

PImage img;
PImage sliderBk;
PImage sliderFd;
int margin = 50;

void setup(){
  size(800,600);
  img = loadImage("tunnel.png");
  sliderBk = loadImage("sliderBk.png");
  sliderFd = loadImage("sliderFd.png");
}
void draw(){
  background(0);
  image(img,0,0);
  image(sliderBk, width/2 – sliderBk.width/2, margin);
  image(sliderFd, width/2 – sliderBk.width/2, margin);
}
Starting a sketch in this way is enables us to see a template of the sketch, and provides us with foundation on which to build. After adding the previous code you should have a sketch that looks something like the following image.
Slider Template


User Defined Functions

Slider Setup

Now that we have a template we can start modifying it, to include interactivity. One of the fundamental components of this sketch is the slider, as the whole idea of changing the color of an image is going to be based on the user's interaction with the slider. As a result we're going to start by setting up the button of the slider, first. The button we will be setting up in this sketch is going to be determined by a range. This range will encompass the clickable area of the button, and because our button is part of a slider the range should automatically be able to adjust itself to accommodate for the button moving around the area that it can be dragged within, by the user.

As detecting the location of the button is a fundamental requirement for this sketch and is something that needs to work throughout the duration that the sketch is running, we are going to create a user defined function that will take care of this requirement for us. Up until now we have been using Processing's built-in functions, which can suffice for many different situations but you will occasionally come across a situation in programming when the language you are coding in does not provide the functionality you need built directly into it's API. This is not a reflection on the language's inability to perform in a certain situation, but rather an intentional quality built into a programming language's design where paradoxically it's limitations, in terms of built-in functionality, can actually extend the languages capabilities. What this means in terms of Processing is we have a set of functions already provided for us, these functions already have definitions associated with them, by creating user defined functions we take these predetermined functions and combine them into new forms of useful, reusable and compact code. This not only allows us to extend the languages capabilities into fields that perhaps even the developers of the language never imagined but also to create our own definitions which suite the specific needs of our own applications in a more practical sense.

Perhaps at this point you are wondering why all programming languages do not share the same set of functions? Many useful, popular programming languages do in fact share many of the same functions we find in Processing, but might be referred to as directives, commands or subroutines amongst other terms depending on which language you are programming in.

Besides the functions that are common amongst many different programming languages each programming language will have certain features (perhaps in the form of functions or some other built in API feature) that will tailor that language to the environment it is best suited, as defined by it's developers and possibly extended in definition by a community, as is the case with Processing. This feature-set that contributes significantly to defining the language's functionality, is intentionally maintained within the languages definition or philosophy as this makes the feature-set easier to contextualize and thereby easier to learn for the programmer using the language, creates an abstraction of commonly used resources within an environment and can significantly improve the language's performance when a user's program is implemented within the context of that philosophy, definition or environment. For example, if you wanted to write your own class for loading and displaying images within a Processing sketch, there is nothing stopping you from doing that, however the PImage class is maintained as a part of Processing's feature-set specifically for those reasons (i.e. loading and displaying images) and as a result is optimized to run as efficiently as possible for the loading and displaying of images. Subsequently the possibility of you gaining any form of performance increase from creating your own class using Processing's built-in functions to serve the same purpose as the PImage class is, unlikely. As a result it's worth considering whether writing your own class or user defined function is applicable or not before setting out to do so. In our case we are creating a user defined function because it will make our code easier to read and not reduce the performance of the application, as our function will contribute to making our program running more efficiently than having the function definition within the draw() structure causing the computer running the sketch to process the function definition line by line every time the draw() structure repeats. With a user defined function we can avoid this by loading the function's definition into memory and calling the definition with a simple function call, as we have been doing with built-in functions.

As we are using Processing's built-in functions, within the environment that they are intended for and best suited to, we will be enhancing the quality of our sketch with our user defined function rather than trying to work against the context that Processing is best suited. However, this is not to say that it is not possible to extend a programming language beyond the definition or philosophy of it's environment, but if that is your intention then you would more than likely consider abstracting a far greater degree of the languages built-in features within a library consisting of multiple classes.

The definitions for user defined functions must exist outside of both setup() and draw() structures if they are to have a greater scope. Our function definition will reside at the bottom of the sketch and contain three main characteristics.

  • 1. Our function should always know whether the user's mouse is over the slider button or not.
  • 2. If the user's mouse is over the slider change the cursor to a hand icon indicating that the button is clickable to the user.
  • 3. Return a value of true or false to the main program, depending on whether the mouse is over the slider button or not.
Although the process of creating a user defined function might seem new to you, it is in fact not really all that different to how we have been using the setup() and draw() function's structures. Bearing this in mind, our user defined functions are going to follow a very similar structure to that of setup() and draw(), for example expressed generically:
datatype functionName(){
   ...function definition
   }
Then within the context of draw():
void draw(){
   ..statements defining draw() structure
                                 }
Now as a user defined function, applicable to our sketch:
boolean myFunction(){
   ...statements defining myFunction
   }
As you can see, not a lot has changed in terms of how a user defined function is structured in comparison to the previous example using draw(). The fundamental difference is that statements delimited within the braces associated with draw() define it's structure and not the function, as the definition of the draw() function exists outside of the sketch we are creating and is something that the developers of Processing have determined. In contrast statements delimited within a user defined function form a function definition, within it's braces. In order to use the function we will then need to include a call to the function from within our main program. This is very similar to the method used to call built-in functions such as rect(), ellipse(), println() and many other functions we have already used. Let's revise our previous example to demonstrate the process of defining a function then calling it from within the draw() structure:
void setup(){
   ...statements
   }
void draw(){
   ...statements
   myFunction();
   }
boolean myFunction(){
   ...statements defining myFunction
   }
From this example you can see that a call to a user defined function is as simple as a call to a built-in function. This has the added benefits of making the code easier to read and removes the function definition from the body of the draw() structure. Now we can reuse the function as many times as we would like within our main program by simply evoking it via it's name, compare this to typing out every statement within it's definition in the main program every time we wanted to instruct the program to do what the user defined function does. Now that we have an understanding of how the function will fit into the program, lets have a look at creating it's definition.