|
Chapter:
Interface Controls: Setting up a Slider
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.
The template
from which the sketch will evolve.
Chapter: User
Defined
Functions
User Defined
Functions
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.
Area in Which
the Slider Button can be Dragged
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.
|
|