Escolar Documentos
Profissional Documentos
Cultura Documentos
You are free to share, and redistribute this publication in whole, but not in part
under the following conditions.
The person identified as the author in the following terms is also known as
Lyndon Daniels and is the owner of the domain name
http://www.lyndondaniels.com
Classified works of the author and a classified work of the author will include
all content and redistributions of the content, that the author has created and is
licensed to the author by de facto or de jure.
This publication refers to the document you are currently reading in it's entirety
and is also a classified work of the author.
1. This publication and all redistributions of this publication must remain as
is, and not in any way be modified.
2. All redistributions of the source code and related material, including but
not limited to the images associated with this publication, that are not
licensed under other terms or are not classified works of the author, also
fall under the same license of this publication.
3. Attribution must be given to the author for all redistributions of the
classified works of the author.
4. Acceptable attribution for redistributions of this publication must associate
the publication/s duplicated as being a classified work of the author.
5. This publication is only to be redistributed digitally and not to be
redistributed in any other format including, but not limited, to print.
6. No capital or commercial profit, monetary or other, is to be gained from
this publication or redistributions of this publication without the author's
consent. This includes, but is not limited to selling and/or trading this
publication or any redistributions.
7. All redistributions of this publication are to be released under the same
license that this publication is released under.
8. The author solely reserves the right to change this agreement at any time.
2
An Introduction To Programming With Processing
Table of Contents
About this Guide......................................................................................................7
Hardware and Software Theory...............................................................................7
What is programming?...................................................................................7
How does programming relate to hardware and software?.....................................8
Abstraction.............................................................................................................10
Speed............................................................................................................11
Efficiency.....................................................................................................12
Disambiguation............................................................................................14
The Unique Qualities of a Programming Language..............................................17
Lower Level Languages and Higher Level Languages.........................................20
An Analysis of a Computer Program.....................................................................22
Commands....................................................................................................22
Expressions...................................................................................................23
A Scalable Software Development Model.............................................................25
1. Plan...........................................................................................................25
2. Research...................................................................................................26
3. Mockup Designs.......................................................................................27
4. Data Acquisition: Sourcing the data needed for your project.................28
5. Filter.........................................................................................................29
6. Clustering and Data Mining.....................................................................29
7. Parsing......................................................................................................32
8. Determining Flow of Control through Stepwise Refinement..................32
9. Implement, Test and Document. Repeat...................................................34
10. Deliver....................................................................................................35
Why Learn Programming using Processing?.........................................................36
Code for Artists............................................................................................36
Java Based....................................................................................................36
Open Source.................................................................................................37
PDE..............................................................................................................38
Source Code Editor.............................................................................39
Build Automation Tools......................................................................40
Compiler and /or Interpreter...............................................................41
Debugger.............................................................................................42
Active Online Community...........................................................................43
Hello Processing....................................................................................................44
Install Processing..........................................................................................44
3
An Introduction To Programming With Processing
Sketches........................................................................................................44
Hello World Program 1.0.......................................................................................45
The println() function...................................................................................46
Syntax and Syntax Errors.............................................................................48
Logical Errors...............................................................................................49
Display Window...........................................................................................49
The size() function. ......................................................................................50
Standardized Coding Practices.....................................................................51
Comments...........................................................................................51
White Space........................................................................................52
Program Notes....................................................................................53
Visual Representations of Code ............................................................................54
The Cartesian Graph and the Processing Coordinate System......................54
Hello Display Window: Hello World 1.1 ....................................................55
The text() function........................................................................................55
Program Notes..............................................................................................56
Formatting Text............................................................................................58
Color...................................................................................................59
Drawing........................................................................................................62
2D Primitives......................................................................................62
The Origin of an ellipse......................................................................63
Aliasing...............................................................................................67
Smile.............................................................................................................67
Angles in Processing...........................................................................67
Editing the smile.................................................................................70
Adding Eyes.................................................................................................73
Drawing Irregular Shapes.............................................................................74
Order, order!........................................................................................79
Finishing the sketch......................................................................................79
Programming Paradigms.....................................................................81
Datatyping Categories.........................................................................82
Primitive Datatypes...................................................................83
Processing's API Datatypes.......................................................83
User Defined Complex Datatypes.............................................84
PImage................................................................................................85
The Design and Layout Program...........................................................................87
Program Notes for Interface01.pde..............................................................87
4
An Introduction To Programming With Processing
5
An Introduction To Programming With Processing
Transforms...........................................................................................................164
Transforming Coordinate Space.................................................................165
A Convenient Animation System...............................................................166
pushMatrix() and popMatrix()....................................................................170
What is the Transformation Matrix............................................................175
A Programmatic Solution For Rotating Wheels.........................................176
An Introduction to 3D in Processing...................................................................179
3D Primitives..............................................................................................180
Guess My Number Game....................................................................................183
Object Oriented Programming.............................................................................184
The concept of a class................................................................................185
The Blueprint Analogy...............................................................................186
Why use Object Oriented Programming....................................................188
Creating a Class..........................................................................................188
A Button Class.....................................................................................................191
Class Name.......................................................................................191
Fields.................................................................................................192
Constructor........................................................................................192
Methods.............................................................................................194
Object Instantiation....................................................................................194
Working with external data..................................................................................197
Attribution............................................................................................................200
Images........................................................................................................200
6
An Introduction To Programming With Processing
What is programming?
A software program is a set of instructions issued to a computer via source code.
Source code is data that usually resembles a text document which is typed in a
specific programming language that is somewhere between a language that
computers can process efficiently and humans can understand. The task of
creating source code is known as programming.
Human readable source code on the left and machine code on the right.
Although source code does not make sense at first glance to someone new to
programming, when compared to it's equivalent in machine code it definitely looks more
appealing.
Computers are designed to react to electrical energy, this energy can be of a high
or low voltage and forms the basic premise of all the complex interactions that
exists between humans and computers. These high and low voltages of electrical
energy are relayed within the circuitry of a computer in very short intervals that
allow us to distinguish one from the other and will eventually be sequenced
together to form a protocol for communicating with computers.
By combining various sequences of 1's and 0's (and ultimately sequences of high
and low voltages) followed by more sequences of 1's and 0's arranged in similar
or different patterns the effect of a continuous flow of energy is created that
results in light or sound or one of the many other capabilities of a computer. The
fact that this flow of electrical energy is broken up into smaller chunks means that
each chunk can be made up of a different sequence of 1's and 0's which could be
used to create the impression of a change or variation which could finally be
perceived as an image moving in an animation or the diaphragm of a speaker
vibrating at different amplitudes which, for example, we ultimately perceive as
the sound of our favorite track.
Although useful, representing data in this way can become quite cumbersome and
error prone. If developing software meant having to learn endless sequences of
binary code, becoming a programmer would be a very daunting task. This is,
however, not the case as we can use a 1 and a 0 to represent a high and a low
voltage we can use a sequence of 1's and 0's to represent more complex ideas
such as a larger number like 155 which can be translated into binary as 10011011.
This form of representation can furthermore be extended to include typographic
characters, which in themselves can be strung together in sequences to create
words, which can in themselves be strung together to issue commands to a
computer through programming. We refer to this process as abstraction.
Abstraction
Representing the abstract concept of something like data in the form of high and
low voltages with a series of 1's and 0's can be quite useful, but still very difficult
to read and understand. Imagine having to memorize all of those sequences in
order to be an efficient programmer. It would take exorbitant amounts of time to
create the simplest of software programs. Fortunately the process of representing
human-relevant concepts to a computer does not stop at binary code. Since we
know that a certain sequence of 1's and 0's on a certain type of machine such as
the sequence 00110101 can be used to represent the number 53 why not make
the sequence 01001000 and 01101001 represent the characters h and i
put together to make the word hi. This is exactly what software developers do,
the process of taking a complex set of data and representing it in a more human-
readable format is known as abstraction. As you can image this makes it a lot
simpler to communicate with a computer, so instead of typing 01001000
01101001 we can simply type hi and thanks to the process of abstraction our
computer knows what to display even though it's reading a sequence of high and
low voltages at the simplest level.
For example if we wanted to draw a circle on a computer screen why can't we just
say to a computer:
There are three main issues to consider here speed, efficiency and disambiguation.
Abstraction 10
An Introduction To Programming With Processing
Speed
One of the main reasons we cannot continue to abstract our communications with
computers to the point where everyday English is used, is due to the speed at
which data can be processed.
As we abstract data in order to make it more human-readable that same data
becomes less machine-readable, and in order for a machine to be able to make
anything useful out of that data it must first convert that data into a machine-
readable format.
All of this abstracting and un-abstracting requires valuable system resources and
energy, system resources and energy that could be used more effectively
elsewhere. Subsequently as we move further away from machine code we require
faster more powerful computers to process the data we create.
It's estimated that under certain circumstances a consumer grade Intel Core i7 3.3
GHz processor can preform 147,600 Million instructions per second, and if
you've ever used one of these processors you'll probably also note that even with
all of that processing power there are certain situations where your computer can
still lag!
This does not necessarily have anything to do with the processor specifically but
simply due to the massive amounts of data that an average modern day computer
is expected to cope with. To put this into perspective consider that 40 years ago in
order to send a man to the moon computers with a clock speed of 0.043MHz and
64Kbyte of RAM were required to run software ranging about 6MB in size, by
today's standards a single modern day home computer could be about 77 million
times more powerful than the computers used to launch a space shuttle, navigate
it to the moon and safely return it to earth such as the Apollo Guidance Computer.
In retrospect the modern day needs we place on our computers can be somewhat
demanding.
Abstraction 11
An Introduction To Programming With Processing
The Apollo Guidance Computer used on Apollo 11 on the left and the NASA Manned
Spacecraft Center on the right. Technology from the 1960's.
Efficiency
Like any natural language, computer languages are built around their
environments. Human cultures have developed languages as a means of
communicating their perceptions of internal, conceptual and external
environments. However what exists in one environment might not be a part of
another. Subsequently, a string of words might be used to describe something that
is uncommon to one environment, whereas in another culture where that thing is
common a single word might be used to describe it.
For example a visitor traveling to Japan in the late 1800's might describe a
specific mode of transport as:
However a person familiar with this mode of transport might have a specific name
for it, such as:
Rickshaw
Abstraction 12
An Introduction To Programming With Processing
Rickshaws are not a common sight in modern times but where a regular mode of
transport in Japan, in the 1800's
Obviously if you are living in an environment where such things are common
using a single word that can effectively describe a whole sentence can be a more
efficient means of communication.
Computer languages are similar in this way, each language is developed to run
efficiently under certain conditions and circumstances yet the same language may
perform less efficiently under a different set of circumstances.
What qualifies as an optimal environment for a computer's programming
language is something that the developer of that language will have to define. An
environment in this sense could be an operating system, the world wide web, a
network, or a combination of these environments. This could also be a large
contributing factor, possibly explaining why there are so many different computer
languages currently in existence each with it's efficiency optimizations for
specific environments.
Currently Wikipedia lists approximately 661 popular computer languages and this
number is constantly growing compared to it's listing of 540 currently spoken
languages (not including their derivatives). Of course there have been many more
spoken languages throughout human history, The Cambridge Encyclopaedia of
Abstraction 13
An Introduction To Programming With Processing
Language estimates this number to be between 3000 to 10000, but then again
what actually qualifies as a language is a whole other topic for discussion in itself.
Computer languages share in this ambiguity. Throughout the comparatively short
history of computer languages, which can arguably be traced as far back as the
first programmable computer, the Z1 invented by Germany's Konrad Zuse in
1938, programmers have seen languages raise to popularity and become virtually
obsolete, one such language is LISP. LISP was in many ways synonymous with
Artificial Intelligence (AI) from the mid 1950's till the early 1970's but gradually
gave way to a dwindling numbers of devotees, who where lured by new
programming paradigms such as object oriented programming and other higher
level programming language concepts, which we will explore in greater detail
later. Although this language never really died, some would say it became
somewhat antiquated. Nonetheless, LISP started to regain popularity in the mid
1990's in a new implementation currently known as Common LISP. Determining
whether a programming language closely based on a previous programming
language should be considered a new language that is able to stand on it's own,
can be a difficult distinction to make and can add considerably to the ambiguity
around what defines and distinguishes one computer language from another.
Processing too, has been subject to this topic of debate as it's roots in the
programming language Java have seen it referred to as a library for Java while
others identify it as stand-alone programming language.
Disambiguation
Computers tend to be very literal. They accept specific instructions more readily
than vague descriptions of what you are hoping to achieve with their help. Such
that a statement like the above mentioned, and repeated below:
Abstraction 14
An Introduction To Programming With Processing
ellipse(56,46,55,55);
Finally I'm sure that if computers had a sense of humour, they would find our
former description of the location of our circle somewhere close to the top
left of my screen to be somewhat uninformed and laughable, not to mention
words such as somewhere and my which might condition a response
Abstraction 15
An Introduction To Programming With Processing
Computers, and subsequently software are used in various fields of medical technologies
where predictability is of the utmost importance. Gambling machines also use software
but in contrast to medical software predictability in the application of the software, is an
undesirable effect for the machine owners.
Abstraction 16
An Introduction To Programming With Processing
Quantum computing models are commonly based on the quantum Turing machine
also known as the Universal Quantum Computer, and even these models can be
related back to the classical Turing machine. Quantum computing models are
theoretical models of how a quantum computer could work once the technology
can be implemented, in much the same way that the Turing machine was a
theoretical model of today's modern computer before it's current implementation.
What about speed, efficiency and dis-ambiguity, are these not also amongst the
unique qualities of a programming language?
Simply put, no they are merely guidelines that programmers may or may not
adapt into the computer languages they develop. However, most useful
programming languages will in some form or another adapt these standards into
their design. There is another form of computer language development that does
not comply with these standards, but the languages that develop from this field do
still comply with Turing completeness, as without this design implementation it
would not be possible to run the language on a computer modern or old.
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>+
+.<<+++++++++++++++.>.+++.------.--------.>+.>.
HAI
CAN HAS STDIO?
VISIBLE "HAI WORLD!"
KTHXBYE
As you can see the language uses what is typically termed as lol-speak as an
integral part of it's syntax and keywording.
However, it's worth considering that the terms higher and lower level
programming languages are relative to the time period in which they are used. For
example when the programming language C (that C++ is based on) was first
introduced in the early 1970's it was considered to be a high level language as it
supported such features as expression evaluation and datatyping, both of which
are programming concepts common to most modern day programming languages.
As technology progresses, new concepts become common place and rapidly
replace older more cumbersome programming designs, till we get to the point
where there are far less people that would refer to an older language such as C as
being a high level language and a lot more people that would refer to it as a low
level language, lacking modern abstractions and less direct hardware interactions.
Processing might one day, also be subjected to such a topic of discussion.
Commands
If you were to compare a statement in computer programming to a sentence in a
natural language, a computer programming statement could be considered to be
somewhat imperative. For example the following sentence in a natural language:
Expressions
Expressions in computer programming languages can consist of single or multiple
different types of data such as numbers, typographic characters and many other
types of data that can be interpreted in various ways according to rules established
by the language you are programming in. The process of how this expressional
data is interpreted is known as evaluation and is similar to the term as it is used in
mathematics. For example 3 + 2 is an expression that evaluates to 5.
Mathematical operators such as +, -, *, etc. work in Processing, as you would
probably expect them to. As the term implies the programmatic mathematical
operator is often interchangeable with the standard classical mathematical
operator in Processing. For example the statement in Processing:
println(5+3);
Consists of two parts, first the function println() which accepts a parameter. In this
case the second part of the statement, the parameter, is an expression and the
expression is 5+3 which evaluates to 8.
One of the basic features of Processing is it's ability to act as a calculator when
computing values separated by mathematical operators such as + (plus), - (minus),
* (multiply), / (divide) and % (modulus). But this inherent ability of Processing
extends a lot further than simply evaluating numerical expressions, as expressions
amongst other types of data can also consist of other typographic characters that
can be evaluated. For example the statement:
println(h + i);
1. Plan
Asking yourself similar questions following this line of thought can help you
visualize the steps, leading to your goals, that need to be taken well before
programming begins.
This process of visualization contributes to creating a conceptual sketch of the
project as a whole and in it's completed form. This idea can then serve as a means
of deconstructing the main concept into smaller workable objectives.
Planning your project through conceptual visualization should be supplemented
with a few rough ideas jotted down on a piece of paper. It's not uncommon to
spend days and sometimes longer periods of time conceptualizing a project before
considering trying to implement any of these ideas.
Depending on the complexity of your project, you may have to further refine the
process of breaking down the smaller tasks into even smaller tasks until you reach
a task or set of tasks that are achievable in the short term. Often these short-term
objectives will not necessarily relate directly to programming but rather to a set of
questions that need to be answered before the implementation of the project can
begin. These questions that you derive from planning your project will form the
basis for the research that follows planning.
2. Research
Addressing the details of how you see your project being implemented should
bring certain technical questions to your attention. Questions such as,
What is the target system you would like your software to run on?
Will my software require additional resources such as plug-ins?
Can the software also be distributed for both online and offline usage?
These are amongst many questions that you might not be able to answer in your
own capacity. The Internet is a seemingly endless resource of information and
probably your best bet when it comes to answering these and many other
technical questions you can think of.
Research might also reveal that the code you are interested in creating might
already exist. If the code is distributed under a license that permits you to reuse it,
then by all means you are encouraged to do so. It is often said that up to 80
percent of a programmers time is spent maintaining already developed code. What
this means is that generally developers will spend a comparatively small amount
of their time developing their own code from scratch and far more time adapting
and modifying already existing code to suit the needs of their projects.
Finally researching your project can also reveal useful sources for data
acquisition, which will be particularly helpful in the fourth phase of your
development model.
The various sites on the Internet have vast resources of reusable code
3. Mockup Designs
Once you have answered the questions you had about implementing your project
consider returning to the notes you created during the first phase of Project
Planning. With the new information you have derived through your research,
refine your ideas into graphical representations of your program.
By now you should have a clearer idea of what your end product should look like.
Avoid prematurely designing your project only to realize your goals where not
realistic given a particular time-frame or some other design flaw, following this
development model in the predetermined order will help avoid such a situation.
Use the mockup design phase of software development to start drawing out
conceptual sketches of your program, in the form of schematic diagrams that
outline the program's flow of control (which we will discuss in more detail in Step
8) and map out the user experience you hope to achieve.
If your software is visually oriented you may wish to create more detailed and
specific designs of your application, for example by designing an interface for a
data visualization program or designing a theme for an online game. Consider
how your designs can contribute to making the user's experience more immersive.
The design phase of software development can also help to identify and even
solve how programmatic implementations of visualizing data might be addressed
through various programming methodologies.
Mockup Designs should include some rudimentary visualizations of the final project and
schematic diagrams detailing the program's flow of control.
Obtain all the data you need to create your software, this data could include
statistical information from books, tabulated data from a website, printed lists of
sales from business owners or even screen-scraped data.
Screen-scraping data is a technique that has been in use for many years and is
used to acquire data from new and old computers alike. In modern times one of
it's most common implementations involves the process of acquiring data that is
in no particular standardized format and commonly sourced from from an html
page of a website. The idea of screen-scraping is that data exists remotely from
the program you are creating, in a format that your program cannot process. As a
result you may need to write another program that serves as an in between
application converting the remote data from an undesirable format to the desired
format of the main software program you are developing.
Screen-scraping is a technique of data acquisition that is differentiated from
parsing, as the data that is being acquired is not intended for another software
program but often intended to be read by a human. Parsing data is something that
happens much later in the software development process and relies on proper data
acquisition. Parsing data is something we will discuss in more detail during the
seventh phase of the software development model.
Acquired data might be in the form of a spreadsheet, a text document, images and many
other different formats that might not, even at this stage, be digital.
5. Filter
Filtering data is simply the process of removing all components of your data that
are not necessary for the proper functioning of the main program. If your data is
in a Processing friendly format such as tabulated data, a multi-line text file, a
comma separated value text file or other similar format the process of filtering
data could be as simple as selecting those components (including spaces and new
line characters in a text file) and deleting them. However, if your data is not in a
Processing friendly format you might have to spend quite a bit of time filtering
out data that is not relevant to your program.
Data mining is the process of identifying patterns within the data you have
acquired. The purpose of doing this is to place the relationships that exist between
the various aspects of your data in a more mathematical context that can
ultimately be used programmatically (in the program you are developing).
Data mining can be done manually or it could be automated. When dealing with a
small data set with a high ratio of inconsistent data types, manual data mining
could be more effective and save you some time.
For example, lets take a hypothetical situation where the owner of a small caf
has asked you to determine what items a customer is likely to purchase together.
The data set you have acquired consists of all the items customers have purchased
in the store over the past year. From that data you could determine that goods can
be divided up into food, drinks, magazines, stationary etc, and then into even
smaller groups like fruit, vegetables, soft-drinks etc. Identifying these groups
would be the first step of data mining and is a process also known as clustering. If
the caf has a large variety of items to choose from the clusters making up the
data could resultantly be numerous, yet the data set as a whole is actually quite
small, only consisting of a single year of purchased items.
In contrast a more established caf that has been operating for several years
proposes the same question to you. In the case of the smaller caf groupings of
purchased items would yield a lower probability of repeating in a shorter period
of time. In contrast the more established caf has a better chance of the same
groups of items being purchased together over a longer period of time.
In the former case manually mining this data set could be more effective because
of the lower probability of the same items being purchased together in a relatively
short space of time. In this scenario the majority of your time would be spent on
clustering and populating the resultant groups after eliminating the majority of
items purchased in that year because they will not fall into any cluster.
However in the case of the established caf although there may be just as many
clusters the values that these clusters are populated with have a higher probability
of being repeated, it might therefore be more efficient to have a computer
program count, cluster and mine the data.
Regardless of whether you choose to manually mine your data or have a software
program do the work for you, you should have a set of data at the end of the
process that can be manipulated programmatically.
Raw Data
Manual Clustering
Digital Results
The process of getting external data into a program, via clustering and data mining.
7. Parsing
The process of transferring data from one computer program to another program
that requires the data in a specific format that is different to the original format, is
referred to as parsing data. The program that converts the original data to an
acceptable format for the main software program to process is referred to as a
parser. Parsing in Processing should return a set of data that your main program
can read directly in a text file, a spreadsheet or other such data format that does
not require any further levels of un-abstraction.
Sometimes, the decision to write a parser might not be an easy choice to make.
Weigh up the amount of time you think it will take to manually convert the data
(as illustrated in the above diagram) compared to the amount of time it will take
you to develop a parser, along with it's overall usefulness (as a parser can
sometimes end up being specific to one application) and try to make an informed
decision based on these factors before jumping head first into the task at hand.
A program's flow of control refers to the order in which statements are run within
in a program. The results of these statements can ultimately determine how a user
interacts with the program by means of various branches of code structures within
your program that the program determines whether to execute or not based on the
choices the user has made through interactions with the program.
Branching is an important part of a programs flow of control as it allows your
program to determine, through a logical decision, what path to take within your
program's code based on various circumstances you have defined for the user.
Planning the program's flow of control allows us to address what we would
ultimately want our users experience of the program to be, and the best place to
start with this process would be to write out a list. This list should contain a set of
instructions that determine how you would like the program to work and
ultimately describe a user's experience of interacting with the program. Once you
have this information remove unnecessary clutter from the list and identify the
key points, keep it concise and short. Once you have this list keep refining it until
you have something resembling a step by step process of the tasks the program
will eventually perform.
Your list at this stage should start to read more like a program rather than a
paragraph written in a natural language. This resemblance is no coincidence and
forms the basis for the process programmers use for developing what is known as
pseudo code. Pseudo Code is somewhere between a programming language and a
natural language and forms the basis for stepwise refinement. During stepwise
refinement rewrite your list so that it resembles something closer to the code of
your program each time the pseudo code is refined.
A Pseudo code example for a guess my number game might look something like
this:
10. Deliver
Once the software has been thoroughly tested it should be delivered. Delivery
could involve something as simple as uploading the software to a website, or as
complex a task as marketing and selling the software. However you deliver your
software, you should always accommodate for the delivery phase of the project
uncovering unforeseen bugs and errors in the software as users test it on systems
that differentiate substantially from that of the systems it was tested and
developed on. As a result maintenance is a crucial part of delivery and an
appropriate time-frame should always be allocated for it within software
development. In some dire situations an entire redesign of the software might be
deemed necessary during the delivery phase, in this case allocating a specific
time-frame for software maintenance might not be adequate to accommodate the
requirements of the development of the software.
There are various options for publishing your software, once it is complete.
Java Based
Processing was originally developed as an extension to the Programming
language, Java and it's roots in Java are still evident today even though Processing
is a language on it's own. Processing's relationship with Java has many benefits,
the two languages share a similar syntax except that Processing can make visual
representations of your code easier to create than Java can (which is more generic
in it's scope of application). Java shares a lot of similarities with the programming
language C (from which it was originally developed) and Java is also one of the
most popular languages currently in use. If you are familiar with Java or C++
(which is also closely based on C) learning Processing should come quite
naturally to you.
The Java programming language is a popular language for software development and is
almost entirely open source.
Open Source
The Processing PDE (which is used to develop Processing code and is something
we will discuss in more detail later) is Open Source Software and it is released
under the GNU General Public License.
You've probably heard the term open source before and are aware that it relates
to something free, but are you aware that the term free as it is used in the
context of open source does not necessarily have anything to do with money or
the absence thereof?
The term free within the context of open source refers more to a philosophy or
methodology rather than a monetary reference and as such is akin to the term
freedom. However as the idea of open source has expanded in modern day
society it is often come to be synonymous with something that is also free of
monetarily related costs. Processing is no exception to this, it does not require a
costly software license such as proprietary software vendors require for the usage
of their products. Anybody can use Processing and the PDE, modify it,
redistribute it and the GNU General Public License ensures that the Processing
PDE will always remain free.
What about the content you create with Processing does that also have to be
free and open source?
The answer to this question is, and will remain to be, no. The content you create
with Processing belongs to you, the creator, and you are free to do with it
whatever you please. If you wish to sell the software or code that you produce
with Processing it is your right to do so. If, however, you do not wish to sell your
work and wish to distribute it freely as open source code there are various options
available to you to protect you and your work.
Amongst these licences is the GNU General Public Licence which protects the
rights of developers of computer programs that wish to ensure that the software
they develop remains free, including all derivatives that are made from the
original software program.
Another such licence is the Creative Commons Licence, this licence allows
creators of media to reserve some rights of their work (if they choose to) and still
legally allows the copying, redistribution and modification of such media if the
author chooses to exercise these rights. Wikipedia is an example of a large scale
organization that licenses it's contents under a Creative Commons Licence and
Linux is an example of popular software that is licensed under the GNU General
Public Licence.
The GNU GPL 3 logo as started by the Free Software Foundation on the left and a
Creative Commons logo on the right both of these trademarks have become synonymous
with the term copyleft.
PDE
The PDE is a popular tool for developing Processing sketches, but another popular IDE,
Ellipse is also quite readily used too.
Of course all code is simply text, so why can't we call any text editor an IDE? An
IDE such as the PDE generally has several special characteristics that separate it
from other software applications.
1. Source Code Editor
2. Build Automation Tools
3. Compiler and/or Interpreter
4. Debugger
The source code editor within the PDE allows you to type code, and access
Processing's API.
Modern day source code editors use color codes to distinguish different types of
data, this is referred to as syntax highlighting and it helps to make code more
human-readable.
Some of Processing's build automation tools. Processing's build automation toolset can
also be extended by downloading tools from processing.org/reference/tools/
Processing compiles your Sketches to Java Bytecode and uses the Display Window
(foreground) for testing and development.
Debugger
A debugger in the context of an IDE is software that examines code either as a
part of the compilation process or before compilation or interpretation in order to
reduce the number of bugs within a program. The term bug in relation to
computer programming is used to describe an error, fault or some means of a
computer program acting in an unexpected or unintended way. The process of
debugging a program is intended to identify these bugs and in some cases assist
the programmer in rectifying them. In the PDE the debugging console consists of
the Message and Text Area and can be found below the text editor and is used
generally for debugging one's own program or allowing the PDE to determine
bugs within a program.
Message Area
Text Area
The debugging console is made up of the Message and Text Area and can be used
to identify bugs and track the associated values of program variables which can be
useful in identifying logical errors.
Hello Processing
Now that we have an idea of what programming is and how we will be using the
PDE to create our own code in Processing lets start coding!
Install Processing
Once you have downloaded Processing, depending on your platform you might
need to install it or just simply uncompress/unzip the package to a location on
your hard disk drive and run it from there. Open the processing root folder inside
which you will find an executable file called processing, double click this file to
start the PDE and you are ready to start programming with Processing.
If however you are unable to run the PDE check that you have permission to
execute/run the processing file and that Java or more specifically the JRE (Java
Runtime Environment) is properly installed.
It is recommended that you use the version of Java that is available from Sun
Microsystems (a subsidiary of Oracle), alternative open source versions of the
JRE and Java Development Kit (JDK) such as OpenJDK do exist but are currently
not recommended with Processing, however this might change at some point in
the future, see the Processing website for updates.
You can get the latest version of Java from http://www.java.com/getjava
Sketches
A program created in Processing is known as a sketch. Processing sketches are
stored in a folder called the sketchbook folder. Being able to identify the location
of this folder will be useful particularly when adding external data to a sketch. To
identify the location of the sketchbook folder within the PDE click:
In the Preferences dialogue box the sketchbook folder can be identified under the
heading Sketchbook location or changed by clicking the Browse button and
navigating to and choosing a new location under the same heading.
All Sketches you create should be stored within your sketchbook folder.
Hello Processing 44
An Introduction To Programming With Processing
println("Hello World");
Press the Run button in the PDE which looks like a play button, or ctrl-r on
your keyboard. The PDE checks, compiles and executes (runs) your program. If
all has gone well you should get a result similar the following image.
The typical Hello World program is many a programmers first attempt at coding.
If your console window has the phrase Hello World printed in it,
congratulations you've successfully completed the Hello World 1.0 program!
Like I said earlier, don't expect to be impressed right away, building a more useful
program is going to take a little more practice. Nonetheless let's have a look at
what is going on here. Firstly you'll notice that the text editor has formatted the
text we input in different colors. The colors denote specific meanings in
Processing, for example in our Hello World 1.0 program:
IDE's such as the PDE that use syntax highlighting make code more human
readable and once you get familiar with what the colors represent you'll find
yourself separating the code you are reading into smaller manageable chunks by
identifying the colors that serve specific functions.
We refer to the sentence typed in the text editor as a statement. Statements are
easy to identify because they always end with a semi-colon and not with a period
(like in English). Our entire Hello World 1.0 program consists of only one
statement which in itself consists of only one command (or function as is the
preferred terminology in Processing).
Processing is a case sensitive language so it is very important that when you type
a function you do not mix it's casing because println() and printLn() will not yield
the same results, in fact the latter will cause an error.
What this means is that we have not defined the function println() we are simply
using it as it has been defined by the people that developed Processing. In other
words we have not told the computer what to do when it comes across println() in
our Hello World 1.0 program. This means that the main body defining how
println() acts in our program is not defined within our program but exists
independently of our program, somewhere else on your computer's hard disk drive
which would typically be where you installed Processing. If we were to take
another look at the Hello World 1.0 program, we can see the function println() is
telling the computer, that the sketch is running on, to print whatever is inside it's
parenthesis. We refer to data we input into a function through it's parenthesis as a
parameter. Some other higher level languages might refer to parameters as
arguments, but Processing adheres closer to C style code by using the term
parameter as an input for functions and the term argument is used to describe
what happens to a parameters through evaluation (which is something we discuss
in more detail later). The parameters our println() function accepts in this case is
the string of characters that make up the words Hello World.
There are two points worth noting here, firstly we have input data into the
println() function by using it's parameter options and secondly it has returned data
back to us, that being what it has printed to the Text Area. When a function is used
in this way we are said to be calling a function. Functions are the building blocks
of Processing and we will be using them regularly and even creating our own.
Syntax errors are common when one first starts programming, but with the help of the
debugging console they can be relatively easy to identify.
As you can see debugging the program in this case is really quite simple, in fact
the PDE tells us exactly where the error is. In larger programs however it might
not be so obvious where the problem lies, although the PDE will try to help you in
tracking down a bug where ever it can. Some useful pointers to remember when
constructing statements in Processing follow, these pointers could help in
avoiding syntax errors.
Always end statements with a semi-colon.
All parenthesis (), brackets [] and braces {} that are opened with their
corresponding left characters must be closed with the right version of the
same character. None of these sets are interchangeable, but some of these
sets are nestable eg (code(code)code), {code(code)code}.
A String of literal characters (such as hello world) must exist between
double quotes. Single quotes are reserved for the char data type (discussed
later).
Look it up in the Processing reference when you are uncertain, which can
be accessed online at http://processing.org/reference/ or from the PDE, for
offline viewing, click :
Help Reference
Of course there are many other points worth noting on syntax and program
structure but we will get to these in due time, for now there's no need to get ahead
of ourselves.
Logical Errors
Logical errors are errors that do not cause the program to halt, crash or throw an
error but will cause the program to act in an unexpected manner or produce
unintended results. Logical errors can therefore be difficult to track down and
rectify because the PDE does not indicate the specific location of an error.
Keeping track of your data through documentation and organizing it into small
manageable chunks can be one method of avoiding logical errors. If your program
seems to have a logical error you might have to use the println() function to track
the values you were hoping to have your program return to you, we'll take a closer
look at this technique when we start creating our own sketches.
Display Window
In our Hello World 1.0 program after pressing the Run button you will notice that
a new smaller window was created. This is the Display Window and because
Processing is a visually oriented language it automatically creates this window
which you will usually use to draw to.
The default Display Window has dimensions 100 pixels in width by 100 pixels in height
and it is automatically created when we run a sketch.
size(640,480);
Note that because we have just entered a statement telling Processing how big we
want the Display Window to be we must terminate this statement with a semi-
colon. You might also have noted that the two values 640 and 480 are separated
with a comma. Do not try to separate parameter values such as these with a semi-
colon because the statement is incomplete at that point and you are therefore
attempting to terminate it prematurely. This will most commonly result in a
syntax error or even worse in a logical error in some cases.
Parenthesis following a function's name is usually reserved for parameters
relating to the function and these parameters are most commonly separated by
commas. This is pretty easy to remember because in English you generally list
items by separating them with a comma in Processing and many other higher
level languages you separate parameter values for a function with a comma.
The two values 640 and 480 have been listed in this particular order because
when Processing accepts parameters of dimensional type like X and Y or width
and height in 2 dimensions, or X, Y and Z or width, height and depth in 3
dimensions it will generally accept values in the order X or width first, Y or
height second and Z or depth third. This is with the exception of a trigonometric
function called atan2() in which Y is read first followed by X due to the special
circumstances of how this function works. Incidentally many other programming
languages besides Processing accept parameters in that particular order for the
atan2() function and in the standard format of X, Y, Z or width, height, depth for
most other functions. Below the first statement type the second statement so that
your code now reads:
println("Hello World");
size(640,480);
Comments
Comments are lines of information inserted between code that informs a person
reading the code of it's purpose and intent. Comments are completely ignored by
the software executing the code such as the PDE and they should be written in
plain and simple English or, what ever your chosen natural language. Consider
that the comments you write might not always be for your personal understanding
but for another person reading your code and therefore should reflect a clear and
impartial explanation of your code.
Commenting your code becomes particularly useful for code that you have not
revisited over a long period of time. Regardless of whether you wrote the code or
not, trying to understand what revisited code is supposed to do after long periods
of time becomes a cumbersome process when it is not commented properly.
Comments in Processing and many other higher level languages (particularly
those that are C based) are indicated with two forward slashes:
Comments that are more than one line long are called multi-line comments in
some programming languages and documentation comments in Processing. They
are indicated by starting the comment with:
White Space
White space is also sometimes referred to as negative space and is the space
between the characters of text that make up the lines of your source code text file.
For example a space, tab or enter/break are all referred to as white space.
Processing unlike other programming languages (such as Python) completely
ignore white space, just like it ignores comments. Programming languages that
ignore white space and allow the programmer to decide how to format their own
code are known as free form languages.
However, just because Processing ignores white space doesn't mean you should,
as using consistent spacing and formatting within your code can make it easier to
read. For example:
println("Hello World");size(640,480);
Although the above code is valid the same program rewritten with proper
formatting can be easier to read (especially when you are in a rush or just glazing
over it), for example:
println("Hello World");
size(640,480);
You can probably imagine how unreadable code could quickly become when
creating more complex programs with hundreds of statements.
Let's revisit our Hello World 1.0 program and retype it taking standardized coding
practices into consideration.
/**
* Hello World 1.0 Program
* by Lyndon Daniels.
* 11/01/2011
* This example program demonstrates how to
* create a Hello World Program in Processing.
*/
Program Notes
Firstly you'll notice that every line in the documentation comment except the first
and last lines of this comment start with an asterisk *. This is not necessary but,
it does make the code look somewhat nicer and many programmers adopt this
fashion of multi-line commenting so I've included it in this program. To
Processing such niceties make absolutely no difference, but to us it makes the
code a little easier to read.
It is standardized coding practice to include the following information in the
documentation comment.
Secondly it's worth noting that the order in which the statements are executed (or
run) has changed. Code written in this way resembles a procedural programming
style meaning that the code is executed one line after the next starting at the top of
the document and working down. As a result it makes more sense to define the
size of the Display Window before any other code is run. Although in this sketch
using this particular order for the two statements is not entirely necessary, it is still
a standard practice amongst many Processing programmers to include the size()
function at the beginning of a program and not at the end. This is also a more
logical approach to writing code in terms of how programs are structured within
Processing. At a later stage you will see that when using the setup() function
starting with the size() function, before any other functions following setup(), is
mandatory.
A point located at
(120,80)
The origin of the Display Window is in the top left hand corner and being the
origin it's coordinates will be (0,0) meaning zero X and zero Y respectively. The
type of grid layout represented in the above image might look familiar to you,
that's because it's based on the Cartesian graph system. This is the name for the
type of graph layout we use in Processing and many other programming
languages that allow us to create and place components of a program (such as
graphical elements) within an area such as the Display Window. Although the
name might sound very grandiloquent it's application is actually quite simple, and
resembles the workings of most common graph layouts used in some of the
simplest forms of mathematics. The main difference between the programmatic
version of the Cartesian graph and the mathematical version of the Cartesian
graph is that in the programmatic version the positive Y axis runs downwards not
upwards as is usually the case in the mathematical representation of this graph.
This actually does not change anything about how the graph system is used, if you
find this to be a bit odd you can think of it as if we were looking at a piece of
graph paper rotated 180 degrees around the x axis so we're looking at the back of
the graph paper and as a result everything drawn on the paper now appears upside
down. As you can imagine nothing drawn on the graph paper has changed just the
way we are looking at it is different.
Understanding how the Cartesian graph system is applied in programming is a lot
easier when we have an example to work with, so lets add to our Hello World 1.0
program by making use of the Display Window in our updated sketch.
text(data, x, y);
For the data parameter in our sketch we will use a string of characters that spell
the phrase Hello World and as you've probably already guessed the X and Y
parameters tell Processing where in the Display Window we would like to draw
the text in terms of X and Y coordinates relative to the Cartesian graph
(superimposed on top of the Display Window, with a bit of imagination).
So let's give it a try, add the following statement to the bottom your Hello World
1.0 sketch and Run it to see the results. If you already have a sketch running you
can press the Stop button (next to the Run button) to stop the currently
running sketch, pressing the Run button again will include any changes you
have made to your sketch:
Using the Display Window gives you immediate feedback for testing your code.
Program Notes
You'll notice that the sketch is now making use of the Display Window, which is
the first step towards creating visual representations of your code. We used the
string of text Hello World in the data parameter. This kind of string is known as
a literal string because it does not require any further processing, within the
program you are creating, to output it's final form of representation, kind of like
the concept of what you see is what you get. The term string, in general, refers
to a list of characters that when grouped together form the contents of a single
string, for example in our case we have a literal string which consists of several
characters 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o' ...etc.
The X and Y parameters are however, looking a little different particularly if you
were expecting them to be numbers. In fact they are representations of numbers in
the sense that instead of inputting a specific number for the X and Y parameters,
we used an expression and in this case the expression evaluates to a number. It is
this number that Processing reads and accepts as the parameter. But what exactly
do the expressions mean and what do they do?
Amongst the many things that Processing is, it is also a very sophisticated
calculator. It has the ability to calculate very complex mathematical formulae and
also is able to do very simple mathematical calculations too.
width/2
The above expression is asking Processing to get the width of the Display
Window and divide (/) it by 2. As you can see Processing has formatted the word
width in blue by use of syntax highlighting, this should immediately indicate to us
that we are not using an arbitrary word but in fact a special word known as a
keyword. Keyword is a generic term we use to describe words that have a
specific meaning for the language we are using that word in. As a result keywords
within Processing do not appear formatted in black. A list of keywords and other
features making up the Processing language can be found in the Processing
Reference (mentioned earlier). The keyword width is a special type of data known
as a system variable. Basically the width system variable stores information set by
the first parameter in the size() function, and as you are aware the first parameter
in the size() function determines the width of the Display Window. In our
program we set the size() function to read:
size(640,480);
How this relates to the width system variable is that every time Processing sees
the keyword width it replaces it with whatever we set the width value to be in the
size() function. In this case since we set the width to 640 the text statement as
Processing reads it would look like this:
The height system variable has the same effect but relates to the second parameter
of the size() function.
So the question is why did we not just simply type the values 640 and 480 instead
of using the keywords width and height respectively? Even further still why didn't
we just type the values 320 for the width parameter and 240 for the height
parameter of the text() function, so that it looks like this:
work. By using an expression with the keywords width and height we can place
whatever we are drawing relative to a position determined by the size() function.
So all we need to do is update the size() function and those expressions that use
the width and height system variables will also update to reflect these changes.
This is a programming methodology that is used often to create versatile and
scalable programmatic interfaces and is referred to as implicit programming.
Although it might not seem entirely obvious to you at this point why it is a
recommended methodology it should become more clear as we explore
programming through the introductory Hello World 1.1 and 1.2 programs.
Formatting Text
You might have noticed that the text is supposed to be in the center of the Display
Window but looks more like it's leaning to the right hand side of the window.
Processing allows us to align text relative to the coordinates we specified for the
text() function's X and Y parameters. The relative positions are LEFT, RIGHT or
CENTER. I have typed them in upper case because that is the format that
Processing accepts them in, when entered as a parameter for the textAlign()
function. So let's fix the text alignment so that it is in the center of the Display
Window, add the following line of code directly above the text statement:
textAlign(CENTER);
If you were to re-run the sketch, the updated version should have the text directly
in the center of the Display Window. As we are using a procedural programming
style to create this sketch, Processing needs statements input in a particular order.
We'll discuss procedural programming and other programming paradigms in
more detail a bit later, but for now it's worth noting the emphasis on the order that
statements are placed in. Placing the textAlign() function before the text()
function ensures that the textAlign() function is run before the text() function
thereby ensuring that by the time Processing gets to drawing the text in the
Display Window it already knows that the parameters that relate to the text's
coordinates within the text() function refer to the CENTER of the text.
The point indicated in the illustration is determined by the text() function's X and Y
parameters. As you can see this point does not move when using textAlign() the text's
alignment to this point is what changes.
Next we'll have a look at the size of the text. Currently it looks like the text is
drowning in a sea of greyness, we're going make the text a little bigger in an
attempt to place more visual emphasis on it, within the composition. The
textSize() function allows us to set the size of text in pixels. Size is input as a
number which is a parameter of the textSize() function.
Add the following statement before the text statement:
textSize(24);
Now we're starting to get somewhere, although white text on a grey background
does not exactly jump out at you. So in keeping with one of the main
processing.org color schemes we're going to use a black background with light
blue text, we'll address these requirements as we examine how to use color in
Processing.
Color
Color in Processing can be input in several different formats such as RGB (Red,
Green, Blue), HSB (Hue, Saturation, Brightness) or Hexadecimal notation. RGB
is the default color mode but this can be changed with the colorMode() function.
Lets have a look at how to change the background color. Add the following
statement to your sketch directly after the size() statement:
background(0,0,0);
The background() function accepts up to three parameters, in our case the first
parameter is the Red value followed by the Green value and finally the Blue
value. As we want a black background all of these values are set to 0, the
minimum value for the current color mode of RGB. The maximum value for these
parameters is 255, this gives you a total of 256 different color values for each
color (remember 0 is also a value). If we wanted a white background we would
enter 255 for each of the three parameters of the background() function. Later we
will have a look at a shorthand version for displaying greyscale colors such as
black, white and various levels of grey.
Now onto the color of the text. The textAlign() and textSize() functions set
parameters not just for the text currently displayed but for all text that is created
thereafter. Until these functions are used again and their parameters are changed
they will remain in Processing's memory and effect all text that is drawn to the
Display Window following their execution.
The fill() function acts in a similar fashion, it accepts color values and will effect
everything that is drawn to the Display Window that has a fill after the function is
evoked. We will have a look at how to modify this behaviour a bit later, for now
lets focus on getting the text to display in a light blue.
In the PDE click:
Tools Color Selector
The Color Selector is accessible within the PDE from the tools menu
The Color Selector dialogue box is useful for finding the specific HSB, RGB or
Hexadecimal color values of a color. In our case the RGB values for the light blue
I'm looking for are R 191, G 233 and B 255. I can now use these values in my
sketch. Before the text() statement add the following statement:
This statement sets the color of the text to light blue color.
Before we continue drawing shapes, lets make a few adjustments to the horizontal
position of the text so that we can have some space to draw a smiley face in the
center of the Display Window. Once again we're not going to input a specific
number into the Y parameter of the text() function, instead we're going to use an
expression so that we can have Processing do the work for us just in case we feel
like changing the dimensions of the sketch once it's complete.
Basically we want to keep most of the text's characteristics as is, but just place it
closer towards the bottom of the Display Window about three quarters of the way
down. We know that the height system variable contains the height of the Display
Window and that dividing it by 2 will return a value that is half the size of the
Display Window's height. As a result we can deduce by use of some simple
mathematics that dividing the height value by 4 will give us a value that is a
quarter of the height of the Display Window. If we then multiply this value by 3
we will have a value that is three quarters in length of the height of the Display
Window. So our expression would look like this:
(height/4)*3
What this reads as is height divided by four then multiplied by three. Just like in
mathematics anything within parenthesis is evaluated before that which is outside
of parenthesis. So the expression height/4 is first evaluated, the answer of this
expression is then multiplied by 3.
Modify your text statement to look like this:
Now we can ensure that whatever the height of our Display Window may be,
Processing will always place our text three quarters of the length down the Y axis
of the Display Window.
Drawing
Processing is a language that was made to create visual representations of your
code really easily, and as a result the developers of Processing have provided us
with pre-configured functions for drawing primitive shapes much like you would
expect in a drawing program. Shapes such as rectangles, ellipses and triangles
amongst others are known as 2D primitives and can easily be drawn to the
Display Window with a simple keyword. We'll start by drawing an ellipse.
2D Primitives
Drawing an ellipse in Processing is easy you just use the ellipse() function that
accepts parameters in the following order:
As you might have already guessed the X and Y parameters indicate where the
ellipse is to be drawn on the coordinate system, and the width and height
parameters indicate the width and height of the ellipse. Please note that the terms
width and height in this context have nothing to do with the system variables
width and height which store information relating to the width and height of the
Display Window. To draw an ellipse in the Display Window add the following
statement to the end of your program:
Once again to find the center of the Display Window I have used the expressions
width/2 and height/2 this returns the X and Y coordinates respectively that will be
the center of the ellipse. Technically a circle is an ellipse with an equal width and
height, which is why we have input 100 for both the width and height parameters
of the ellipse() function. I've also chosen to enter explicit values for the ellipse's
width and height parameters, as I don't want the dimensions of the ellipse to
change even if I do decide to change the dimensions of the Display Window.
Using this hybrid style of mixing implicit programming and explicit programming
can sometimes lead to logical errors, so you should always be certain of what you
are doing when using this approach to programming.
ellipseMode(CENTER);
The CORNER parameter for the ellipseMode() function allows you to create an
ellipse by specifying the X and Y parameters of the ellipse() function as the top
left hand corner of an imaginary bounding box that surrounds the ellipse. In other
words the X and Y parameters of the ellipse() function are used to determine the
top left-hand corner of the bounding box from where the width and height
parameters of the ellipse() function are used to extend to the maximum width and
height of the ellipse.
To use this creation method for ellipses type the following code:
ellipseMode(CORNER);
At this point you might be wondering what the purpose of functions such as
textAlign() and ellipseMode() are since we can simply place an element of a
sketch, such as an ellipse, where ever we want in the Display Window using the
function to draw the element's X and Y parameters.
width/2
Ellipse 1 Ellipse 2
In the above diagram the method used to determine the origin of the second
ellipse is to divide the width of the first ellipse by two, then add this value to
whatever half the width of the second ellipse is. This calculation would return a
value that would equate to the X parameter of the second ellipse. The Y parameter
of the second ellipse would be the same as that of the first ellipse.
width
Ellipse 1 Ellipse 2
When using the CORNER option in order to determine the second ellipse's origin
we would simply need to know the width of the first ellipse and add this value to
the X coordinate of the second ellipse. This calculation although different from
the previously mentioned example would yet again return a value that would
equate to the X parameter of the second ellipse. The Y parameter of both ellipses
is the same.
As you can see although the visual results are the same, that is two ellipses with
their edges touching at the same places, how we solve the problem is different
depending on how the origin of an element is determined.
The relationship between the placement of one element with that of another is
Change the name of your sketch to something more meaningful and save it.
Processing will create a new folder in your sketchbook folder with the name of
your sketch, in this folder Processing will save a .pde file which is the source code
file we are currently working on. It's also worth noting that if you have added any
additional files to your sketch such as images or other external resources
Processing will also copy the those files to a folder called data, which will be a
subdirectory of your sketch's folder. In the PDE saving a file with a different
name is akin to creating a new sketch. This is something to be aware of as you can
inadvertently increase the amount of data on your hard disk drive by duplicating
sketches with different names if you are unaware of this feature. Adding external
content to a sketch is something we will get into a bit later when we start adding
images to the sketch. The sketch should currently look something like this:
/**
*Hello World 1.2 Program
*by Lyndon Daniels.
*This example program demonstrates how to
*create a Hello World Program in Processing.
*/
Aliasing
You might have noticed that the ellipse is looking a bit pixelated and not very
smooth this effect is known as aliasing and is often counteracted with the effect of
anti-aliasing which creates the impression of a smoother looking rendering.
Fortunately in Processing we have a function that is made exactly for the purpose
of making the contents of a sketch look smoother. Add the following statement to
the first part of the sketch just after the size() function:
smooth();
The smooth() function does not accept any parameters but still requires
parenthesis, it's also worth noting that although using the smooth() function will
make your geometry appear smoother it might come at the expense of slowing
down the rate at which your sketch runs. By default a Processing sketch will try to
update itself 60 times in one second (if a function such as draw() is used), this
allows for smooth looking motion and almost instantaneous interactive results.
We are creating what is known as a static sketch which does not involve any
interactivity or animation, so using the smooth() function in this situation should
not greatly influence the sketches performance.
Smile
The ellipse needs a smile in order to make it into a smiley face. We will use an arc
to draw the smile, the main reason we are using an arc is firstly to introduce the
arc() function but more importantly it's an opportunity to explore working with
angles in Processing.
Angles in Processing
In mathematics angles are usually measured in either degrees or radians,
Processing and many other programming languages generally accept both units of
measurements but prefer the latter. One of the main reasons most programming
languages prefer measuring angles in radians is because radians work well with
the value known as PI (pronounced py rhymes with try) which can be
represented in mathematical notation as . PI is a number that has the
approximate value 3.1415927 (calculated to seven decimal places). The actual
number that PI represents in programming is not quite as important as it's
mathematical equivalent, subsequently we will generally never need to remember
what this number is in programming but rather what the language we are choosing
to use it in, represents it as. In Processing the value of is represented by the
mathematical constant PI. To Processing and many other programming languages
a constant is a value that does not change, as a result it makes sense for the
Processing representation of pi to be a constant. Pi has always been the same
value long before Archimedes approximated it's value in the 200's BC as it's
implication in the construction of many architectural wonders of human ingenuity
from as far back as the Egyptian pyramids and even further back in human history
suggests, and it's highly unlikely that pi could be used to represent any other
value. So what does it mean?
Pi is the ratio of the circumference of a circle to it's diameter, and pi radians is
also equal to approximately 180 degrees. Pi can be expressed mathematically as:
= C/d
For example if we had the angle 270 degrees and we needed this in radians so that
we could use it in our Processing sketch we could either use the mathematical
constants Processing provides us with and type the following expression:
PI + HALF_PI
(PI/180)270
Remember that because there is no mathematical operator between 270 and the
right closing parenthesis character the value of what is in parenthesis must be
multiplied by 270, this will return the equivalent of 270 degrees in radians.
radians(270);
This will convert 270 degrees to it's equivalent in radians. One of the main
reasons why angles in Processing are converted to radians is because all
trigonometric functions that require angles as an input accept these values in
radians.
Getting the hang of working with radians at first might be a little tricky if you are used to
degrees, but if you think of them in terms of pi they can be a lot easier to work with.
You are already familiar with the first four parameters X and Y determine the
origin of the arc and this can be further controlled with the ellipseMode()
function. The width and height parameters represent the dimensions of the arc in
much the same way that these parameters, determine the dimensions of an ellipse.
The last two parameters you might not be familiar with, the start and stop
parameters determine the beginning and ending angles of the arc. For example,
we are attempting to draw a half circle to represent a smile which, as you know,
can be expressed with angles between 0 and 180 degrees. Secondly, because we
want our smile to resemble the shape of a horseshoe pointing upwards we will
start it at 0 and end it at PI (which is approximately 180 degrees). This means that
the arc will be drawn between the start and stop parameters and the other
parameters are used to determine the dimensions of the arc and the arc's position.
Add the following statement to your sketch following the ellipse statement:
You might have also noticed that when you comment out a statement, syntax
highlighting causes the statement to turn grey. This means we can now run the
program and Processing will no longer draw our ellipse, until we uncomment the
statement by deleting the comment tokens. To comment out a block of code
select the lines of code you wish to comment out and in the PDE click:
Edit Comment/Uncomment
This will toggle your selection between a commented state and a standard block
of non-commented code. Commenting and uncommenting is just simply a
convenient and easy way of testing or debugging a program by including and
excluding statements easily without having to retype out a statement that has been
deleted or using an operating system's clipboard.
Commenting out the ellipse statement reveals that the arc is drawn with a fill.
To remove the fill we will use the noFill() function. So between the ellipse
commented statement and before the arc statement add the following code:
noFill();
If you were to run the sketch at this point it might look as though the noFill()
command has removed the arc completely from the Display Window, but this is
not the case. In Processing 2D primitives such as triangles, arcs, quads, ellipses
and rectangles are actually made up of both a fill and a stroke. We've already seen
that the fill can be controlled with the noFill() function, the fill() function and that
Processing's default setting is to draw all 2D primitives with a fill.
The stroke is the outline that surrounds all 2D primitives and looks like a line of
various inclinations, dimensions and weights. The stroke can also be turned off
with a function similar to the noFill() function, as you might have guessed it's
noStroke().
To turn the stroke or fill back on or to simply edit an existing stroke or fill we use
the stroke() or fill() functions. Both of these functions accept parameters in the
form of a grayscale color, an RGB value or a RGBA value by default. You can
also control how Processing accepts parameters for these two functions with the
colorMode() function. We'll be changing our black stroke (which seems to have
disappeared, because it's camouflaged into the black background) to a red smile.
Add the following statement after the noFill statement:
stroke(255,0,0);
The stroke() function can be used in a similar way fill() is used but for editing the
color of lines.
This statement simply informs Processing that any strokes that are drawn after
this statement is run will consist of a color that is fully red, with no green and no
blue in other words (255, 0, 0). Now we have a smile floating above our text, so
we're going to uncomment the ellipse statement and when we run the sketch our
smile is restored to it's elliptical face.
The smile is looking a little too high up on the face so we're going to move it
down, this is easy because we already have the style of the smile so we just need
to offset it's y position. To do this we are going to edit the arc() function once
more to the following state:
Notice that because we are using an inverted Cartesian graph adding 10 to the
arc's current y value actually moves the arc down, inversely if we subtracted 10
we would move the smile up. At first getting used to this system might be a little
tricky, but it's certainly worth noting because this type of graph system is used in
many graphics based software packages.
Adding Eyes
What's a smiley face without eyes. Add the following code to your sketch after
the previous arc statement:
stroke(0,0,0);
fill(0,255,0);
ellipse(width/2-15, height/2-15, 20, 30);
ellipse(width/2+15, height/2-15, 20, 30);
As we changed the stroke to red in our previous statement to draw the smile, we
need to change it back to black in order to draw the strokes encompassing the
eyes so we add the second stroke statement to our program to change the stroke
color back to black. Having also turned the fill off for the arc statement we need
to turn it back on for the eyes and set it to green, we can conveniently achieve
both of these tasks with stroke() and fill() function calls.
Let's take a look at the X and Y parameters of the ellipse functions that actually
draw the eyes and how the expressions used in these statements have been
modified. For the eye on the left's X parameter 15 has been subtracted, and for the
eye on the right's X parameter 15 has been added.
When working with expressions like this it's important to remember that certain
mathematical operators have precedence over others. For example * and / have
precedence over + and -, this means that the expression width/2-15 is actually
being evaluated like so (width/2)-15. This effect is known as operator
precedence. Finally the Y positions of the ellipses have been offset by subtracting
15 and rendered less circular in form and more oval-shaped by making their
height's greater than their widths.
vertex(width/2, (height/4)*3-35);
vertex(width/2-70, (height/4)*3-35);
vertex(width/2-80, (height/4)*3-25);
vertex(width/2-80, (height/4)*3+5);
vertex(width/2-70, (height/4)*3+15);
vertex(width/2, (height/4)*3+15);
//Notice how the X parameters are mirrored from this point, The Y
//parameters do not change from their mirrored counterparts
vertex(width/2+70, (height/4)*3+15);
vertex(width/2+80, (height/4)*3+5);
vertex(width/2+80, (height/4)*3-25);
vertex(width/2+70, (height/4)*3-35);
vertex(width/2, (height/4)*3-35);
endShape();
Although at first this might appear to be a little confusing and a lot of typing,
upon closer inspection you may notice that it is in fact repetitious and more of
copy, paste and modify effort. So lets have a look at what's going on here.
In the above illustration we can see that beginShape() has started recording the
various points that make up the irregular shape of the speech bubble, which we
subsequently define for the beginShape() and endShape() functions one vertex at
a time. The first vertex is located along the center of the Display Window's X axis
followed by the Y parameter for this vertex which is 35 pixels above the center of
the text that says Hello World. Of course we could just as easily have typed in
the values explicitly such as:
vertex(320, 325);
But using this method of explicit programming does not leave much room for
changes and could subsequently lead to the extremely time consuming task of
changing each explicitly determined point in your program manually, if it is
deemed that such a change is necessary at a later stage.
From the next vertex on we can use the Display Window's width and height
system variables and the text's X and Y position as a starting point from which we
can place every vertex that follows relative to the position of the previous vertex
and in relation to the other elements making up the program.
Using this method of implicit programming also makes it easier for us to mirror
the coordinates of the irregularly shaped speech bubble across the center of it's
own Y axis. As you will notice once we get to the vertex marked as the point
where the shape starts to mirror itself it's just a simple question of copying and
pasting the previous code excluding the first vertex statement, changing the
negative values associated with the vertices' X parameters to positive values, and
reversing the order of the statements in a mirrored fashion.
Using an implicit style of programming can also make patterns in your code easier to
identify.
This can save quite a bit of time as it is not necessary to work out the specific
coordinate of each vertex individually all we need to know is half of the
coordinates that make up the shape and since we are mirroring the shape on the X
axis we simply change the negative values associated with X to positive values.
Once we have all the vertices mirrored on the right hand side (excluding the last
vertex) we can then start to plot the points of the arrow head of the speech bubble
that points to the smiley face implying that it's saying Hello World.
This part of the shape is pretty straight forward because we already know that we
will need three points to make the triangular shape of the arrowhead and we
already know the Y positions of two of these points because they will be at the
same height of the first and last vertices of the speech bubble. So it's just a case of
simple trail and error to determine what looks best in adding the remaining
parameters to the next three vertex() functions. Once that's complete we can close
the shape by placing the last vertex at the same location of the first vertex.
Finally to end the shape you must use the endShape() function.
You might have noticed that the image on page 75 has a speech bubble with a
thick blue outline around it. This is due to the strokeWeight() function, and it
controls how thick or thin a stroke is rendered. It accepts a number as a parameter,
I don't recommend setting this number too high as you might find it causing
undesirable effects on your sketch. The higher the number the thicker the stroke.
Add the following statement before the statements that draw the speech bubble:
strokeWeight(8);
You might remember that in order to have a black outline around the face's eyes
the stroke() function had to be set to an RGB value of 0,0,0 (or black), so
although the strokeWeight() function has done it's job by increasing the thickness
of the stroke surrounding the speech bubble, the effects of it might not be so
obvious. Changing the stroke and fill colors are functions that you might need to
run several times within a single sketch to achieve the desired result which is
precisely what we are about to do. Add the following statement before the
previous statement:
stroke(50,127,255);
This changes the stroke color for the speech bubble to a little bit of red, about 50
percent green and full blue. In order to change the fill color of the speech bubble
to white you must add the fill() function before the drawing of the speech bubble.
A statement to determine the speech bubble's fill color as white would look like
either one of the following two fill statements:
fill(255,255,255);
//or
fill(255);
Either option is perfectly acceptable, the latter is used for determining a greyscale
value from 0 (which is black) to 255 (which is white). When dealing with
greyscale values, including black and white, the latter is the more widely adopted
method as only one parameter is needed as opposed to three parameters used to
determine RGB color values.
The Stroke Caps are over lapping on the first and last vertex of the speech bubble
in the current version of the Sketch.
The section of the speech bubble where the first and last vertices of the shape
overlap has a slight blemish caused by the default stroke caps setting. Stroke Caps
determine how the end of a stroke is drawn for example is it round or flat, and
they become most evident when dealing with heavily weighted strokes such as the
stroke surrounding the speech bubble. Fortunately we have a function in
Processing for controlling how stroke caps are drawn, as you might have guessed
the function is called strokeCap() and it accepts the parameters ROUND,
SQUARE or PROJECT.
Different types of stroke caps in Processing, which are very similar in effect to that of a
vector illustration package.
Remember that Processing is case sensitive so you must type the parameter's
name with the correct casing. ROUND is the default parameter that causes strokes
in Processing to have a beveled end, SQUARE will end the stroke with a flat end
and PROJECT will tend to render the end of the stroke slightly past it's destined
coordinates, like ROUND but with a flat end. We are going to use SQUARE to
fix the speech bubble. Add the following statement after the strokeWeight()
function call:
strokeCap(SQUARE);
Order, order!
We now have a speech bubble, but where's our text gone to? If you added the
statement to draw the irregular shape to the end of your sketch, Processing would
have drawn the speech bubble over your text. Your text is still there just
underneath the speech bubble, obviously this is not the desired effect. So we are
going to reorder things in our sketch, with some simple cut and paste commands.
Cut the three statements that relate to rendering text to the Display Window
textAlign(), textSize() and text() to the end of the sketch so that they are run after
the speech bubble is drawn. If you were to run the sketch at this point your text
will still appear to be missing, but in fact is still there it's just that because it's
been drawn after the speech bubble it's also been effected by the fill() function
issued to change the speech bubble's fill to white. White text on a white
background isn't very legible so we'll change the text to another color, I'm going
to make my text the same color as the speech bubble outline by adding the
following statement before the text is drawn and after the speech bubble is
rendered:
fill(50,127,255);
To add the final touches to the Hello World 1.2 Program we're going to get
Processing to display an image for the sketches background instead of the black
background. This exercise will also be our first introduction to the programming
paradigm known as Object Oriented Programming and the concept of data
typing.
The first thing we need to do is set our sketch up to accept external data in the
form of an image. With the PDE still open, via your computer's operating system
open a file browser and locate the file named smileBkg.png. From your file
browser click and drag the image file into the sketch you are currently working on
in the PDE.
When you have dropped the file to the sketch Processing, will confirm this in the
debugging area by printing the phrase One file added to the sketch. If you get
this message you are ready to start using the image in your sketch.
Adding an Image to a Sketch in Processing is easy, just click and drag it into the PDE.
The simplest way to use external data, such as an image, in your Processing
sketch is to ensure that the data exists in a folder called data, which must be a
subdirectory of the location where your sketch is saved. For example, we set up
Processing to store all sketches in folder called Sketchbook, stored within this
folder will be the sketches we create with the PDE. I've called my Hello World
1.2 Program smile so within the Sketchbook folder is another folder called
smile and within the smile folder is the file smile.pde (which is my Hello World
sketch) and another folder called data. When we dropped the image into the
PDE Processing automatically created the data folder, and placed a copy of the
smileBkg.png file in it. If you would like to view the contents of this folder you
can access it from the PDE by clicking:
Or simply navigating to the folder with your operating system's file browser.
When transporting your Processing sketches from one computer to another you
must always retain this directory structure of having your data folder as a
subdirectory of the location where your processing sketch resides, which should
always have the extension .pde.
Programming Paradigms
A programming paradigm is a fundamental style of programming, the term is
often confused with programming methodology which is a style of addressing a
problem within a program. But perhaps a better way to understand what a
programming paradigm is, would be to look at some examples of it.
Many modern day software languages offer at least two fundamental
programming paradigms, most commonly amongst these two choices are the
Objected Oriented Programming Model and the Procedural Programming Model
(Procedural programming is also sometimes referred to as imperative
programming). Like the name implies imperative programming is a programing
paradigm that is used to determine a set of commands and/or steps a computer
program must take in order to reach a desired state. It's worth knowing that the
Object oriented programming model is often contrasted to the procedural
programming model because of the fundamental differences (which we are about
to discuss) that separate these programming paradigms.
Procedural Programming can be summarized as a style of programming where the
program is tailored to suite the data as opposed to Object Oriented Programming
which is more akin to a style of programming where the data is tailored to suite
the program. So what exactly does that mean?
As mentioned earlier, programs are made up of statements and one of the things
statements are made up of, is commands. As you might recall a command in
programming, much like in a natural language, is used to communicate a
directive. In this way procedural programming is a list of statements telling a
computer what to do. At this point you could be thinking Isn't that the purpose of
all programming i.e. telling a computer what to do, so how then does procedural
programming differ from any other programming paradigms?
In one way or another we use programming to represent data, whatever that data
may be the amount of tea compared to the amount of coffee consumed over the
past ten years world wide, determining the choices a character has in a game then
choosing to act on those choices or something as simple as representing a set of
alpha numeric characters on a screen that spells your name. Whatever way you
look at it creating a program requires data. In procedural programming we use the
tools provided to us by the language we are programming in to break down that
data into smaller data types that the language predetermines for us and then use
those representations to create a program that ultimately represents the larger
original data set programmatically. This can be contrasted to object oriented
programming where we use the tools provided to us by the language we are
programming in to create our own data types for which we determine their
meanings and use these new data types to create a program that ultimately
represents our original data set.
Either way you look at it the results of both procedural and object oriented
programming can produce software that is modular in design, meaning that
components of the data that can be represented with either one of these paradigms
can be dynamic or changing, or even completely non-existent. Designing software
that does not have to be rewritten each time the data that it represents changes or
is removed from the main program entirely, is a challenge regardless of which
programming paradigm you might use and it is this key feature of software design
that determines how modular the main program is.
Object Oriented Programming since it's earliest inceptions in the late 1950's
programming language LISP has consistently seen various implementations on
the concept of modularity amongst other features such as encapsulation (which
we will discuss in more detail later), contributing to it's modern day popularity. As
software development needs have become more demanding and larger data
structures are required for abstractions, Object Oriented Programming due to it's
inherent ability to represent complex data structures, modularly has grown in
popularity to meet the needs of software developers creating these sophisticated
programs. That is however not to say that procedural programming lacks
sophistication. To summarize programming paradigms in a utilitarian sense,
people tend to address problems in different ways, programming paradigms
accommodate for these shifts in thought and conceptualization.
Datatyping Categories
Organizing the data that we represent in a program can be a difficult task unless
you have some sort of system that can be applied at a higher level to abstract the
complex interactions that a machine must facilitate in order to make the data we
interact with more accessible to us. Datatypes make the process of organizing the
various forms of data that exist in our programs more accessible and categorically
meaningful to us. What qualifies as a data type varies from one programming
language to another and also forms the basis of what differentiates the two main
programming paradigms we have been discussing, Procedural Programming and
Object Oriented Programming.
In Processing there are three main categories of data types that we will be dealing
with Primitive datatypes, Processing's API datatypes and User Defined Complex
datatypes.
Primitive Datatypes
Primitive datatypes form the building blocks of all the different types of data
accessible within Processing. As a result these datatypes are immutable, meaning
that we cannot modify their definitions by use of Processing.
int
Numbers can be represented as Primitive datatypes and most commonly fall into
the primitive datatype categories of int or float. An int data type is an abbreviation
of the word integer and is used to create categories of numbers within a program
that represent whole values such as 0, 1, -58, 6000000 and so on. In Processing an
int can range in value anywhere between 2,147,483,647 to -2,147,483,648.
float
A float (or floating point number) is a data type used to categorize numbers that
have a decimal point. Numbers such as 1.5, 3.0, -56.91 and even numbers such as
3.40282347E+38 in SI (the International System of Units) notation qualify to be a
float primitive datatype.
char
Processing's API Datatypes are special datatypes defined within the Processing
API that may or may not be found in other languages, but will make fulfilling the
requirements of creating sketches (including data visualizations, end-user
interactions and many other features commonly found in sketches) easier and less
repetitive for the programmer by abstracting base-level computational
interactions.
The PImage datatype is an example of a Processing specific API Datatype that
makes the task of using images within a sketch much simpler for the programmer.
The String data type is a Processing non-specific API Datatype, meaning that
although this datatype is common to many different programming languages it is
not a primitive datatype as it uses characteristics of both int and char datatypes,
this makes it more complex than a primitive datatype. Processing's API Datatypes
are usually quite easy to identify as the keyword used to invoke them usually
starts with an upper-case character, this is a common standardized programming
practice that is used to identify Classes which are the fundamental building blocks
of Object Oriented Programming and are an inherent quality of Processing's API
design.
PImage
The PImage Class defines useful information about the image we are loading into
our sketch and allows us access to this information without having to understand
how it has extracted this information from the image, such as it's width, height or
how many pixels make up the image and what color each pixel is. This is one of
the benefits of Object Oriented Programming, being able to abstract complex
code by referencing it with a single keyword, the name of the object.
We are going to create a new software object from the PImage class, and we will
give this new object a name. It is important that the name that we assign to the
new object is unique so that when ever we want to use that object we can refer to
it by it's name and Processing will know exactly which object we are referring to.
Amongst the prerequisites of using an image file as a background in Processing is
that the image must be in either gif, jpg, png or tga format and must have the
same dimensions as the sketch in which it will be displayed. If you have had a
look at the smileBkg.png file you might have noticed that the dimensions do not
match that of our current sketch. However, we are in luck because we have been
using an implicit style of programming to specify the X and Y parameters within
our sketch so changing the dimensions of our sketch at this late stage in the
programming process is actually not going to be problematic. If, on the other
hand, we had used an explicit style of programming we would have a problem as
all of our had work to keep the graphical components of our sketch in the center
of the display window and aligned with each other would now be lost and fixing it
would mean having to go through every statement where X and Y coordinates
where used and changing them to accommodate for the new sketch size.
Since we are loading our image as a background we are going to add the
following statements before the background statement near the top of the sketch.
To create or as it is referred to in OOP-speak, instantiate a new object from the
PImage class add the following statement before the background statement:
PImage img;
This statement tells Processing that we want to create a new software object
which has all the properties of the PImage datatype and we would like to call this
new object img. Our new software object has inherited the properties of the
PImage class which allows us to access information about the image we would
like to associate with the object named img (which we have instantiated from the
PImage class), without having to know how it goes about accessing this
information. However, what our new object does not know yet is what image we
would like to use in our sketch and refer to as img, in other words what image do
we want to associate with the img object? Add the following statement after the
previous PImage statement in order to associate the image smileBkg.png with the
img object:
img = loadImage("smileBkg.png");
This statement tells Processing the name and location of the image file we would
like to use and refer to as img throughout the rest of our sketch. Because the
image file has already been dropped into our sketch Processing has placed a copy
of the file in our data folder. Processing will expect to find whatever image files
we are using in our sketch in this folder. The loadImage() function can also accept
a URL as a parameter if you wanted to load an image that is not located locally on
the same station that your sketch is running from.
Now that we have the image loaded into our sketch we need to tell Processing
what we would like to do with and, where we want to place it and finally to draw
the image to the Display Window.
To display the image in our sketch we're going to modify the background
statement and Processing will now render the image to the Display Window when
we run the sketch. Modify the background statement to read:
background(img);
This statement simply tells Processing that we would like to use the object named
img as a background. Later we will have a look at compositing images on top of
each other in addition to using them as backgrounds.
To briefly recap on the steps we took to load an image into our sketch
1. Drop the image into the PDE
2. Instantiate a new software object from the PImage Class
3. Load the image file into the new software object
4. Instruct Processing to display the image through it's identifier (name).
Using what we've learned thus far from this guide, you should be able to construct
a program that sketches a prototype for an interface to an online portfolio
similar to the interface01.pde sketch.
The Create Font tool can be used to ensure that font's you have used in your sketch will
display correctly on other users' systems regardless of whether they have the same fonts
installed on their systems or not.
The Create font dialogue box appears in which you can scroll down to and select
the font you would like to package with your sketch. Choose a size you would
like to have the font displayed at by specifying a numerical value in the size field
(values are in pixels). The size value determines the size of the characters of the
font that will be exported as bitmaps, and although you can change what size you
would like to display a font as in your sketch even after it's been exported it is
recommended that you use the font in your sketch at the same size that you
exported it as for best results. Up-scaling a font is not recommended and as a
result if you do plan on using the font in different sizes it is best to use the Create
Font tool to export the font at the largest size and use Processing font() function to
down-scale the font only when necassary.
When you are happy with your selection click ok and Processing will create a
file with the extension .vlw in your sketch's data folder, it is important that you
know the exact name of this file as you will need to load it just like you loaded an
image into Processing by using the PFont class and instantiating an object from it.
To use the font in your sketch, you will need to follow a procedure very similar to
that of loading and displaying images. Start by creating a new object that will
serve as the container for your font, by adding the following statement:
PFont myFont;
PFont is a special data type in Processing used for storing fonts, in a similar way
that PImage works with images. myfont is the name we have chosen to create an
object that inherits the qualities of the PFont class. Next you will need to load the
font into your sketch, to do so add the following statement:
myFont = loadFont("FancyFont-Bold-42.vlw");
The loadFont() function accepts one parameter that is the name of the font,
including it's extension, enclosed within double quotes. We will then need to
specify what font we would like to use to display text in our sketch, because you
are permitted to use more than one type of font in a sketch the next statement tells
Processing which font it is you would like to render the text that follows this
statement:
textFont(myFont,42);
The textFont() function accepts two parameters, the name of the PFont object you
have created (which is myFont in this case) and the size at which you would like
the text that follows to be rendered at. All the calls to the text() function (which is
used to render text within a sketch) that follow this statement will use the
parameters set in textFont() for rendering text, until textFont() is called again with
a different set of parameters. Finally you can draw the text to the Display Window
with the text() function:
You might notice something strange about the literal string parameter for the
text() function. Between the words my and Processing are the characters \n.
This is known as an escape character and escape characters are not rendered
within a sketch. Escape characters are used to format text before they are drawn to
the screen. In my Interface01 sketch there is a break between Welcome to my
and Processing Portfolio, this is because the \n is known as a new line escape
character and has the function of inserting a break between characters. This
prevents having to use the text() function twice, first to render the text Welcome
to my then adjust the Y parameter in the next call to text() to render the rest of
the sentence. Using a short-cut like this can save your program some overhead,
but also comes with the expense of making your code look a little less readable,
particularly if you have never seen an escape character before.
Another place where a short-cut has been used in the Interface01 program is in
the statement where multiple instances of the PImage class have been created as
holders for images that will be used throughout the rest of the sketch. The
following statement demonstrates this:
PImage mGoat;
PImage banner;
PImage bkg;
PImage button;
As you can see using a shortcut in this way can save you quite a bit of typing and
can actually make your code even easier to read.
void setup(){
code...// This code block will run only once,
// at the start of the sketch.
}
void draw(){
code...// This code block with run repeatedly,
// throughout the duration of the sketch.
}
A typical active mode sketch structure with the setup() and draw() code blocks
highlighted
Up until now our sketches have not been interactive or included animation. The
use of the active mode structure, by means of the setup() and draw() functions,
allows us to truly access the powerful features that programming offers to
developers and extend our sketches with interactivity, animation, logic and a host
of other features that we will explore.
First, we'll start by having a look at how setup() and draw() change our sketches
by making them more dynamic.
PImage img;
One of the methods, that works, in a sense, just like a typical function would of
our main program, of img could be to resize the width and height of the image
that we have associated with the img object for example:
img.resize(50,100);
This statement would resize the width and height of the image associated with the
img object to 50 and 100 respectively. In this sense the resize() keyword used in
relation to the img object would be a method of the img object. Just like a
function, a method's definition can also exist independently of the main program
and in this case it does, as the resize method is defined within the PImage class
which as you are aware is not defined within our sketch but exits as a part of
Processing's API. So as you can see the term method is very similar to that of
function in terms of the purpose they both serve. However, it is important that the
terms are not used interchangeably as they can lead to ambiguity and confusion.
As I have mentioned before Processing is a language on it's own and not just a
library consisting of classes for Java. If it was, referring to setup() and draw() as
methods would be perfectly acceptable because they would be methods of a class
defined within the Processing library for Java. Although Processing can be used in
this context we are not using Processing as a library for Java we are using
Processing as a language that is independent of Java until implementation, this is
relevant because although Processing relies on Java currently for it's
implementation theoretically there is nothing stopping Processing from being
implemented within other environments. Within this context Processing is a
language and not exclusively a library for Java. What that means is that, setup()
and draw() within this definition cannot accurately be discribed as methods of a
class that has been instantiated but rather as functions of a programing language.
This concept can be extended even further because setup() and draw() are such
fundamentally important functions within Processing, that they could be placed in
a category of their own and described as structural defining functions, because
they alter the structure of a program so dramatically.
93
An Introduction To Programming With Processing
img = loadImage(myBitmap.png);
The purpose of assignment is to make whatever is on the left side of the operator,
equal the value that is on the right side of the assignment operator. In this way
assignment can be a relatively straight forward or complex procedure. For
example if we have a simple value such as a number or typographic character on
the right of the assignment operator, the assignment of that value to whatever
exists on the left is relatively straightforward because we're just simply telling
Processing to make whatever is on the left of the assignment operator be the same
as that which is on the right and then store those values in the computer's memory.
However if we have an expression, a command or both on the right of the
assignment operator the task of assignment can become more complex not only
for Processing but also for us to keep track of.
In the previous example we are assigning the value of the bitmap image that
exists in our data folder called myBitmap.png to the img object, by using the
loadImage() function.
But, what exactly is going on in this assignment statement, as the computer that is
running the sketch sees it?
Firstly the computer running the sketch must process the call to the function
loadImage() this requires processing power and secondly the computer having
realized what the loadImage() function requires after processing it, now accepts
the parameter myBitmap.png.
In order for it to accept this parameter it has to allocate a certain amount of it's
memory to load the image. Computers have a limited amount of memory and the
larger the bitmap image's size (in terms of kilobytes, megabytes, etc.) the more
physical memory they require to be loaded into a sketch. As a result the computer
must try to allocate memory that has not already been assigned to another value
and does not request more memory than is necessary. All of this work performed
by the computer is performed without us having to know anything about the steps
it must take to display the image in our sketch.
As you can see, although this assignment statement was relatively easy for us to
create, the steps that our computers must take in order to make what we are
requesting possible is actually not so straightforward.
The setup() function can be very useful in a situation such as this, because the
previous statement is so resource insensitive it is not the kind of statement we
would like to have repeated throughout the duration of our running program.
Doing this will simply cause a bottleneck effect on the resources of the computer
running the sketch and ultimately cause the sketch's performance to drop, as the
computer struggles to keep up with our requests.
The setup() function as a result will run only once throughout the duration of a
program, and is designed to keep system performance at optimal speeds so that
our sketches can run more efficiently with the code that does need to be repeated
such as that of the draw() function.
Using the setup() and draw() functions in Processing is actually quite easy, and in
the following example we'll use them for the purpose of getting the user's mouse
position in terms of X and Y coordinates and then use the println() function to
print these values to the debugging console.
Enter the following code and run it. Don't be alarmed when Processing starts to
print line after line of information to the console, this is actually the desired effect.
Move your mouse around in the Display Window to see the values update in the
console view then close the Display Window when you are done:
void setup(){
//setup the size of the sketch
size(510,300);
}
void draw(){
//print the mouse positional data
println("The Mouse's X position is " + mouseX +
" The Mouse's Y position is " + mouseY );
}
The Mouse's X and Y position is updated every 60th of a second and printed to
the console
Experimentation
The setup() function must always precede the draw() function, and is used to
define the initial properties of the sketch such as the size of the sketch. The size()
function when used within an active mode sketch must always follow the setup()
function before any other statements. If we had images to display in our sketch
assigning them to object variables would generally be done within the setup()
structure, we'll have a look at this technique a little later.
The draw() function repeats once every 60th of a second by default, but this
behaviour can be changed with the frameRate() function which would be called
within the setup() structure too.
Within the println() function you might have noticed something a little different,
two new commands, mouseX and mouseY are system variables in Processing that
store the X and Y position of the mouse when it is over the window the sketch is
running in. When used within a sketch they simply return a number that is either
the respective X or Y coordinate of the user's mouse. It is also worth noting that it
is necessary for the println() function to be repeated within the draw() structure
because whenever the user moves the mouse within the Display Window a new
value for the mouse's X and Y position replaces the old value for the previous
position of the user's mouse. If this command was within the setup() function
Processing would only print the mouse's X and Y values once and not again.
Experimentation 97
An Introduction To Programming With Processing
Although this is perfectly valid code it, is not very useful to us because if the user
has moved the mouse since the start of the program (when setup() ran for the first
and only time) the current position of the user's mouse would not have been
updated thereafter. The statement to update the mouse's X and Y position would
not have been repeated so according to the sketch the X and Y position of the
mouse would be the same as when the program first started, this is obviously not
the case if the user has moved the mouse.
The mouseX and mouseY system variables can be used to add interactivity to a sketch
Experimentation 98
An Introduction To Programming With Processing
After running the sketch you should be able to recreate an image within the
Display Window like the preceding image. The line() function is used to draw a
straight line in Processing and accepts four parameters in the form of the starting
point of the line's X and Y coordinates and the end point of the line in terms of X
and Y coordinates. In our sketch we made the starting point of the line the origin
of the sketch, that is (0, 0) or zero X and zero Y and we made the end point of our
line equal whatever the mouse's current X and Y values are. There are infinite
ways of using the mouseX and mouseY system variables to create interesting and
interactive qualities within a sketch, and they don't always have to relate to
positional data as in our previous statement. For example, if we wanted to use the
mouse's X value to determine the greyness of the line we could add the following
statement to effect the strokes color, which we will add before the line()
statement:
stroke(mouseX/2);
Remember that the stroke() function used in this way with only one parameter
accepts values between 0 to 255 which relates to the greyness of the following
strokes that are drawn. So because the size of our sketch is 510 pixels across our
mouse's X position will, in the context of this sketch, return a value between 0
and 510 for the mouseX system variable, as any value greater than this would
mean that the mouse is no longer positioned over the sketch. Bearing this
information in mind we can use what we know about the width of the sketch and
divide this value by 2 so that we never receive a value greater than 510/2 or 255
which is white, when input as a parameter of the stroke() function. This is a very
simple example of mapping one value to another but Processing conveniently
provides us with a more sophisticated method of mapping values by use of the
map() function which we will look at in more detail later.
By linking one value to another we can start to create some interesting effects, within our
sketch
Experimentation 99
An Introduction To Programming With Processing
Try experimenting with the mouseX and mouseY system variables until you are
comfortable with them as we will be using them quite regularly in our following
sketches. For example try combining the previous sketch with an ellipse that
attaches itself to your mouse and has an interactive transparency feature.
As you might remember, the order in which commands are issued in many
programming languages is really quite important. So when Processing reads your
code it is important that commands are issued to Processing in the correct order,
for example we would not want to have a command that updates the color of an
ellipse's fill after the ellipse has been drawn to the Display Window. We are
already aware of this from our static mode sketches, however in active mode even
if you do have a fill() command after an ellipse() command to update that ellipse's
fill your results sometimes might be more forgiving.
As active mode requires that the code within the draw() structure is repeated there
remains a history from one execution of this code to the next. What that means is,
even though the fill() command is issued after the ellipse() command when the
program repeats the next time around Processing will remembers what the value
of the fill was set to in the previous execution and updates your ellipse in the
current cycle to match that command of the previous execution. Currently this
might not seem like a big issue to you because you are ultimately getting the
results that you want, but this is at the expense of creating ambiguity within your
program. This could also lead to possible errors later in the programs
development cycle, and is therefore not a recommended methodology for several
reasons.
Firstly, it is not clear within the order of such a program why the fill() command
is being called. Whereas if the fill() is issued before ellipse() it is clear that it is
Experimentation 100
An Introduction To Programming With Processing
meant to effect the color of the fill of the next ellipse that is drawn to the Display
Window.
Secondly, if there are other values depending on your ellipse's fill being a
particular color at that point in time, and these values precede the fill() command
these values will always be calculating their results based on the color of the
ellipse's previous fill color and not the current color for that particular cycle. This
color only becomes evident the next time the program repeats, and by then the
visual representation of the fill will no longer match the values that are used in
calculations preceding it. This could lead to inaccurate calculations such as
division by zero which could cause your program to halt or even worse to crash.
Needles to say that although you can get the results you are hoping to achieve by
structuring your program in a manner that contradicts appropriate ordering, you
might in effect be causing yourself more problems in the long run than necessary.
Always consider how the ordering of your commands and structure of your
program can be improved.
Experimentation 101
An Introduction To Programming With Processing
Datatyping
You might have noticed something else that's new about the active mode structure
of a sketch, that being the usage of the void keyword preceding both the setup()
and draw() functions. The keyword void is used for defining functions that do not
return a value, all other user defined functions must return a value of a certain
type of data. The setup() and draw() functions differ significantly from their roots
in the C/C++ main() function in that they are not intended to return a value of any
type of data to the main program, the mandatory usage of the void keyword
ensures this. We'll have a look at user defined functions in more detail later.
The process of assigning a type to data is known as datatyping. There are many
different types of data within Processing we have already had a look at some of
the categories that the different types of data within Processing can be broken
down into, but what exactly are these different types of data and how do they
assist us in programming?
One of the datatypes we're already familiar with is the PImage datatype. We know
that if we want to use a bitmap image in our sketch we must first assign it to the
object we instantiated from PImage. That new object that we have then created
has inherited all of the properties of the PImage datatype amongst which is the
ability to resize the image, get information about the pixels that make up the
image and what the alpha and color values of each pixel making up the image is.
We did not have to tell Processing what a pixel is or how we would like to use it,
these definitions already exist predetermined within the PImage class and other
Processing API features, and will resemble a body of code that exists somewhere
on your computer (probably in the location you installed the PDE). Now that we
have a new object instantiated from the PImage class it has inherited all of the
properties that define that particular type of data, in other words the new object
we have created has been datatyped as PImage.
But in order to understand what is really going on here we need to look a little
deeper...
Variables
Variables are common to most programming languages as they provide us with a
means of storing data within the memory of a computer without having to know
the process the computer has taken to store that data and where exactly within the
computers memory that data has been stored. This provides us with a convenient
method for accessing data, without having to understand the complexities of
Datatyping 102
An Introduction To Programming With Processing
manual memory management as might be the case in lower level languages. Lets
have a look at how variables play a part in helping us store data.
Using variables is pretty straight forward and can be thought of as a two step
process, 1.) Declaration and 2.) Assignment.
The second step we are already familiar with, which could imply that you might
have already used variables in your previous sketches, and this is in fact correct.
You have used both steps of variable creation in your previous programs as
variable assignment relies on declaration.
Declaration is the process of giving the data you are representing in your program
a name, and is a process that involves datatyping (preformed by the programmer)
and memory allocation (performed automatically in most higher level languages).
So lets have a look at a declaration in the following statement:
PImage img;
int myVar;
The keyword int in this statement is used to datatype a variable with the
properties of type integer.
Integers are primitive datatypes that must be whole numbers, such as 0, 5, -1,
2430 and so on. They cannot have a decimal value. All we are doing at this stage
is giving the variable a name, that being myVar, we have not given the variable a
value yet the value would effectively be an integer, and associating the variable
Datatyping 103
An Introduction To Programming With Processing
println(myVar);
However this is getting ahead of ourselves a little, as you might remember one of
the main reasons we use variables is so that we can associate data with them
through the process of assignment. In the previous example we have declared a
variable but not given it a value, so lets restructure the example to be more
meaningful:
In the second line of the above code listing we have used assignment to associate
the numerical integer value of 5 with our variable, and in the third line we have
put the variable to work by getting println() to read it's value back to us. You
might also notice that variables are never placed between double quotes, like the
literal strings we have been using with the println() function up till now.
What makes variables so special is that we can get them to store any value that is
valid to it's datatype, change this value then re-use it in another statement with the
new value, without the help of variables a simple task like this would be very
impractical. This is particularly useful as we never have to know what address the
variable we are using has in memory, Processing takes care of that for us.
Here's an example of the changing (or variable) nature of a variable:
int x; //declaration
x = 5; //assignment x is now 5
x = x + x; //assignment expression x is now 10
println(x); //Prints 10
The value you assign to a variable can be changed when ever you instruct your
program to do so, often through use of the assignment operator. As a result in the
previous example although we started with the variable we called x having a
value of 5 assigned to it in the second statement, in the next line we added it's
current value of 5 to itself then reassigned that new value of 5 + 5 (on the right of
Datatyping 104
An Introduction To Programming With Processing
the assignment operator) back to itself (on the left of the assignment operator),
making x equal 10. Using this simple method we have represented two different
values of 5 and 10 with only one variable, x. That does not mean that x could be
either 5 or 10 it simply means that at the start of the program x was 5 and by the
end of the program x ended up being 10. By having one variable that can be used
to represent many different values we can save on memory as, at any given time,
that variable can only have one value associated with it, meaning that if a new
value is assigned to it like in our previous example that new value will replace the
old one and so on. Not only does this make our program run more efficiently but
it also makes keeping track of the representations of the different data in your
program more manageable.
Variable Scope
An important aspect of working with variables is that they can be accessed at
various places in your program so that the values associated with them can be
referenced (in their original form) or changed and then referenced. For example
you can reference a variable in one statement, run a couple of other statements
then choose to reference the original variable again even with all the code
separating the the first reference from the second, Processing will still remember
what the variable you are referring to is.
But not every variable is accessible from every place within a program. Variable
scope refers to the enclosing range within a program that a variable can be
accessed. For example variables defined within the setup() function are not
accessible to the rest of the program, which is why setup() is best suited for
assignment in terms of dynamic programming. As such there is a scope in which
variables can be defined such that they are accessible from every place within a
sketch, we call this the global variable scope.
Datatyping 105
An Introduction To Programming With Processing
As you can see in the previous image x cannot be accessed from within draw()
because it has been initialized within the setup() function. Initialization is the
process of combining variable declaration and assignment into one statement.
Take the following declaration and assignment statements for example:
int x; //declaration
x = 20; //assignment
Although the latter statement might save you some time in terms of typing, it is
something you should be careful of doing when taking variable scope into
consideration, as when this method of initialization is used within setup() it is
going to create variables that are not accessible (and therefore cannot be
referenced) from outside of the setup() structure.
The proper way to address this issue would be to declare the variable outside of
both draw() and setup(). Generally it is not a good idea to declare a variable more
than once because this could cause unnecessary overhead on your program, as
such, declaring variables within the draw() structure is best avoided whenever
possible. Nonetheless, declaring variables within the draw() structure sometimes
cannot be avoided and as a result is considered to be valid code.
Each time you use a datatyping keyword for variable declaration Processing
thinks you are trying to create a new variable, and does not perceive the statement
as updating the already existing variable with the same name.
Programming like this tends to look ambiguous and difficult to read. However, as
already mentioned, there are times when declaring a variable within draw() is
unavoidable, or in some cases can even contribute to the readability of your code.
Basically, what this boils down to is that where you choose to declare your
variables is your choice, but there are also certain rules in place that if you are
aware of, can make this decision somewhat easier for you.
Lets restructure the previous example using a more readable format that takes
variable scope into consideration:
Datatyping 106
An Introduction To Programming With Processing
Datatyping 107
An Introduction To Programming With Processing
void setup(){
//size() always first in setup
size(800,600);
//use setup() for assignment wherever possible
doors = loadImage("doorsClosed.png");
}
void draw(){
//draw the image
image(doors,0, 0);
}
When loading images into an active mode sketch, it's important that you declare
the images object variable name outside of both setup() and draw(). This makes
the object accessible as a global variable so that it is now within the scope of both
setup() and draw(), this is important because we will need to access the new
object variable from within both structures. It is advisable to use assignment in
the setup() structure so that the image is loaded into the new object variable only
once, placing the loadImage() function, within an assignment statement, for the
new object inside the setup() structure, ensures this.
Finally once we have set the image up for rendering we can use the image()
function, as usual, to display the image in the sketch. It's worth noting that
because the image is static, it is not possible to see that the image is in fact being
redrawn to the Display Window approximately 60 times per second, when the
sketch is run and although this might sound like a lot of excessive usage of system
resources, most modern day computers should be able to handle this framerate for
one image with relative ease. However, we will take a look at how to reduce the
amount of times Processing cycles through the draw() structure per second, when
image(doors, 0, mouseY);
Now when you run the sketch your image will start to move up and down in the
Display Window because the value that the image() function is reading for it's Y
parameter is the system variable mouseY. As you might recall mouseY stores the
mouse's current Y position. If your mouse has moved, the new Y position of your
mouse will replace the previous value that was stored in the mouseY system
variable of the previous run-cycle, and as a result the image will be drawn in a
new position.
But why does the image appear to be streaking across the Display Window?
S treaking
We have told Processing to repeatedly draw the image by placing the image()
function within the draw() structure, as a result the streaking effect is actually
caused by the ghosting of the previous image having been drawn to the Display
Window and not being erased before the image is redrawn in a new position for
the current frame. To fix this effect we need to tell Processing to first clear the
Display Window before drawing the image again in the current position. We can
do this quite easily by adding the background() function before the image
statement, thereby clearing the Display Window of the the image's previous
rendering. Add the following statement before the image() statement in the draw()
structure:
background(0);
Now when you run the sketch it should act in a more predictable fashion. I've
chosen black as a background color but you can choose any color. There are
however several areas that can be improved on,
firstly the way in which the image moves in relation to the mouse is not
very interesting to interact with. It would seem that the image appears to
be stuck to the mouse.
In order to fix this we will make the image lag slightly behind the mouse
and then slowly catch up to the position of the mouse by creating a new
variable that we can use to control the speed at which the image moves.
This will create the impression of the image's movement being affected by
friction.
Secondly because the image's origin is it's top left hand corner, when we
move the mouse down we tend to expose the background above the image,
so we'll also make the image react to the mouse's Y position in relation to
the center of the image and not it's top left hand corner. This reduces the
possibility of exposing the background and makes the image more of a
focal point.
Finally we will constrain how far the image can move along the Y axis so
as to completely eliminate the possibility of exposing the background.
Friction simulation
Friction is the force that resists the relative motion of something else (like an
object, a gas, a liquid, etc), and is the effect that we will be attempting to simulate
(in a simplified form) in this sketch. By moving the mouse up and down within
the Display Window the image should try to follow the mouse and slow down as
it approaches the mouse, this will create the impression of friction.
We'll start by creating three new variables. Add the following statements at the
top of the sketch, below the PImage statement:
The float keyword is used for declaring floating point variables. Floating point
values are numbers that have a fractional part, that is to say they are numbers with
a decimal value such as 1.0, -2.9865, 10000.423, 0.0000001 and so on. Floating
point numbers will tend to require more memory for storage so if you can get
away with representing the number data as type int and not compromising the
integrity of your program you should consider doing so.
We are using the float datatype in this program because we will be using division
in one of our expressions. This is important because true division will always
return a number with a decimal value, if you were to use an int instead of a float
or double depending on the programming language you are using, you risk having
datatype conversion errors or truncated integers returned. As a result when you
use division in your expressions always consider whether representing that data
with an int is a good idea or not.
The three variables yPos, difY and frict are going to be used to represent three
different values of which only one will remain constant throughout the duration
that the program is running. The only variable that will remain constant is the frict
variable. As a result the frict variable has been initialized through a declaration
and assignment statement outside of both setup() and draw() structures. This
value will control how fast or slow we would like the image to move towards the
mouse.
Creating a variable such as this within the global variable scope, will give us
access to the variable within both setup() and draw() thereby allowing us to tweak
the value associated with the variable and have it update throughout the program.
This will save us some unnecessary work in the long run. When the sketch is
complete setting the frict value high will cause the image to appear to have more
resistance when moving towards the position of the mouse ultimately making the
image move slower, and setting this value low (but not to zero, because division
by zero is not permitted in mathematics, as is the case in programming) will cause
the image to appear to have no resistance and move almost immediately to the Y
position of the mouse.
The variable yPos will be used to store the Y position of the image and also
update the Y position of the image, and the variable difY will be used to store the
difference between the mouse's Y position and the center of the image. Both of
these variables will need to be updated regularly for each of the cycles of the
draw() function, this is because each time the user moves the mouse the position
of the image must change as a result of difY now being less or more depending on
whether the mouse is closer or further from the images center.
yPos = height/2;
If we wanted to make sure that yPos is returning the value we are expecting it to
return then we could add a println() statement within the draw() structure to test it.
The statement would look like this:
println(yPos);
If the program was run at this stage it should print a value that is representative of
the center of the Display Window's height. Using this method of tracking
variables is a common technique for debugging a program and provides an easy
and convenient method of understanding how the program is changing and
updating values internally. When you are happy that the program is returning the
results you were expecting you can simply comment out the println() statement
and resist deleting the statement until the program is fully completed, tested,
documented and functional.
Determine what the Y position of the users mouse is (mouseY) and from this
value subtract the sum of the Y position of the image (yPos) and half of it's height
(doors.height/2). The latter value (yPos + doors.height/2) describes the position of
the image with relation to it's center.
The image's movement will react to the mouse's relative position to the images center.
As the mouse's Y position is less than the center of the image, the image is forced to
move up.
Then we're going to assign the sum of those values (yPos + difY/drag) back to the
yPos variable.
Finally we will use this value to determine the position that the image is currently
rendered at.
The results of this method is that the image will start moving fast towards the
mouse then get slower as it approaches the mouse.
x = x + 5;
x+=5;
Both statements evaluate to the same answer, the latter is a more commonly used
method for this type of statement, as this kind of statement is so commonly
implemented in programming. Some other examples of augmented assignment
follow:
x = x -5;
//Can be expressed in augmented assignment form as:
x-=5;
x = x * 7;
//Can be expressed in augmented assignment form as:
x*=7;
/= and %= can also be used in augmented assignment. As you can see the
following statement used in our sketch is an example of augmented assignment:
yPos += difY/drag;
//Can also be expressed as:
yPos = yPos + difY/drag;
At this point if you were to run the sketch, your image still would not move in
relation to the two statements you just added to your sketch. However if you were
to add a println() statement for the yPos variable you would see the numbers
indicating that the code is working. It's always a good idea to test an
implementation before fully integrating it into your program when ever possible,
with the println() function.
In order to create a link between these two statements and the Y position of the
image we would need to place the variable yPos into the Y parameter field of the
the image() function. However, if we where to do this our image would continue
to move beyond it's boundaries and display the sketches background. Fortunately
we have a function in Processing that allows us to stop values from getting too
high or too low.
Constraining Values
The constrain() function works by clamping a value between a range that you
specify. It accepts three parameters, the first parameter is the value that you want
to constrain and in our case this would be the variable yPos. The next parameter is
the minimum value of the range you would like the value constrained between
and the final parameter is the maximum value for the range that the value yPos
will be constrained between.
Modify the image() statement to match the following code fragment, and
complete your sketch:
At first this statement could look a little intimidating, but in fact you already
know what's going on in most of the statement and it's only the Y parameter of the
image() function that is somewhat different.
As you are aware the image() function accepts three parameters, firstly the object
variable containing the image (i.e. doors), secondly the value of the X position
you would like the image rendered at and finally the image's Y position.
This expression when evaluated, returns the value of the image() function's Y
parameter. The constrain() function clamps the yPos value so that the image is
never raised above the value returned from height doors.height and never
lowered below 0, this will be the point where the image's origin matches the
Display Window's origin.
The area which the image's motion is constrained to, exists above and outside of what is
visible within the Display Window.
Of course if you think this example could be easier to read, you're probably right.
For example you could declare a global variable to which you assign the previous
expression to within the draw() structure. The additional overhead that such an
action would create in this example is minimal, for example:
...
float imageY;
void setup(){
...
doors = loadImage("doorsClosed.png");
...
}
void draw(){
imageY = constrain(yPos, height- doors.height, 0);
image(doors,0, imageY);
}
Branching
When we write a program we generally don't want the script to do the same thing
every time we use it. Often, what we want is a script that is versatile in it's
application. Excluding this versatility would make the interactive software we
create predictable and boring. We therefore use certain built-in structures that help
us control the way in which our programs run, allowing us to skip blocks of code
and execute other blocks of code before others in a non-linear way. This can be
done in a dynamic and changing sequence based on the users input, the logic of
the program, the time of day or whatever other characteristic we choose to model
into a program. The process of creating this dynamic ordering that determines the
programs flow of control is what is referred to as branching.
Branching is also referred to as conditional programing and is the process of
creating code that has the ability to choose a certain path based on a set of
conditions stipulated in the program but which can also be influenced by
conditions outside of the program, such as a user interaction. The conditional
statement is syntactically a single statement made up of multiple branching
statements. What determines the course of action that the program follows is a
boolean value of true or false (which we will discuss in more detail later) returned
from a comparison within the structure of a conditional statement.
conditional
if(expression1){
do statements if() statement
} structure
keyword
Branching 119
An Introduction To Programming With Processing
Multiple if statements can be used within the same program and even within the
same structural function such as draw(). They are identified by the if keyword
followed by parenthesis containing a conditional. The conditional for an if()
statement will always return a value that is true or false through the process of
evaluation which can be based on a comparison expression. We'll have a look at
the process of comparison a bit later, for now we'll focus on the boolean datatype.
Data that can either be true or false is a primitive datatype known as a boolean.
Just like all primitive data types booleans can be declared with a keyword
followed by a variable name.
The expression within the conditional of an if() statement must, through the
process of evaluation, return a boolean value of either true or false in Processing,
in order for the program to continue. In other programming languages the
conditional can also evaluate to a 1 or a 0, with 1 being representative of true and
0 being representative of false. The language's compiler/interpreter will then
convert the 0 or 1 to a boolean equivalent value through a process known as type
casting. Processing, however, requires that the conditional evaluate to a boolean
value of true or false, so that automatic type casting can be avoided. As you are
aware 1 and 0 are of type int in Processing and the Processing does not
automatically convert them to boolean values. In the event of your conditional
evaluating to an int or datatype other than a boolean you will receive a cannot
convert datatype error. Later we will have a look at how to use Processing's in-
built ability to prevent automatic type casting to our advantage. Although
automatic type casting is favored in some programming languages it is usually an
inherent legacy feature of a lower level language that a current higher level
language is based on. In Processing type casting must be done explicitly with a
type casting function.
Branching 120
An Introduction To Programming With Processing
Relational Operators
Writing if() statements becomes really interesting when more than one expression
is used in the conditional. Up until now we have only used a single expression as
a condition.
Branching 121
An Introduction To Programming With Processing
In the previous image the highlighted area indicates a single expression for the
conditional in the form of a variable called myBooleanVar which has been
datatyped as a boolean, in this case the variable has been assigned the value of
true, therefore the conditional evaluates to true. This simplistic approach to using
if statements can be useful in some situations but, the ability to use comparison
within a conditional statement extends it's usefulness much further.
Comparison
A comparison requires at least two entities (which are most commonly numerical
values and/or expressions in Processing, but can extend into strings in some other
programming languages) and a relational operator (also known as a comparison
operator in other programming languages). Comparisons included within a
conditional, just like the previous expressions we've been working with, must also
evaluate to a value of either true or false. Comparisons simply instruct the
program to evaluate the two expressions on either side of the relational operator
and compare the results of the two expressions with each other and from this
comparison return a boolean of either true or false. Relational operators include:
< less than
> greater than
== equality
<= is less than or equal to
>= is greater than or equal to
!= inequality
Let's have a look at how we can use the a relational operator to compare two
values.
Branching 122
An Introduction To Programming With Processing
(5>1)
(5<1)
This comparison is read as five is less than one. This is not true and the
program will return a value of false. Comparisons are not limited to simple int's
and can be used to evaluate the relationship between complex expressions.
Following is an example of a conditional code fragment comparing two
expressions:
The Y position of the mouse is compared with the system variable, height when it
is divided by two, which as you know is the middle of the Display Window's
height. A conditional such as this can be more useful in terms of creating dynamic
content because when placed within the draw() structure the value that is returned
each time the code is repeated may or may not be the same as the previous return
value, depending on whether the user's mouse is in the bottom half of the Display
Window or not.
Lets have a look at how we can use this code fragment in a sketch. Try running
the following code. Start by hovering your mouse over the top of the Display
window then moving it to the bottom of the Display Window and watch what the
console prints.
void setup() {
size(300,300);
}
void draw() {
if( mouseY >= height/2){
println("Mouse is in the bottom half of the sketch");
println(mouseY);
}
println("Mouse could be in either half");
}
Branching 123
An Introduction To Programming With Processing
The program starts, as always, by running the code within the setup() structure
then moves onto draw(). Within draw() it runs the if() statement which instructs
the program to check whether the mouse's Y position is greater than or equal to
the height of the Display Window divided by 2. For example in our sketch the Y
parameter of the size function within setup() is 300 so if the user has placed the
mouse at a Y position that is anywhere between 150 and 300 then the conditional
will return a value of true as these values are all greater than or equal to 150 (i.e.
height/2). It's also worth noting that the relational operator that has been used in
this comparison is the >= (greater than or equal to) operator, which means that the
value that is returned on the right side of the operator is included within the range
of values (as the minimum value in this range) that will cause the conditional to
evaluate to true. What this means is that if you want a value of false returned the
mouse must be within the range of 0 to 149.
If the conditional returns a value of true meaning that the mouseY system variable
has a current value of 150 or anything between 150 to 300, the program then runs
the code delimited within the if() structure, which in this case tells Processing to
print "Mouse is in the bottom half of the sketch" and then print the value of the
mouseY system variable. The program then moves on to the rest of the code
within the draw() structure. This tells the program to print "Mouse could be in
either half", as this statement exists outside of the if() structure, it will be
evaluated regardless of whether the if()'s conditional is returned as true or false.
if() structures can increase the amount of options that can be programmed into a sketch.
Branching 124
An Introduction To Programming With Processing
The program then loops back to the beginning of the draw() function and goes
through the code within the draw() structure again as it has reached the end of the
draw() structure at this point. If the users mouse is not within the bottom half of
the Display Window this time the comparison conditional will return a value of
false, the program will then skip the code delimited within the if() structure that
tells it to print "Mouse is in the bottom half of the sketch" and move onto the rest
of the code within the draw() structure that once again tells it to print "Mouse
could be in either half".
As you can see branching provides us with a very easy way of changing the
behavior of our program by including variables within a comparison that in
themselves are dynamic.
We could get Processing to run a set of statements based on whether the mouse is
in the upper half of the Display Window. But what if we wanted Processing to run
a specific set of statements when the mouse is in the upper half of the Display
Window and a different set of statements when the mouse is in the bottom half of
the Display Window?
Using an if statement by itself to fulfill this purpose would not be possible, but
fortunately we can extend the if() statement into an if() structure with the else
keyword.
Use of the else keyword within an if() structure ensures that even if the
conditional returns a value of false that the code that follows else (which is still
within the if() structure) will be run, before the program exits the if() structure and
moves on to the rest of the statements making up the program. This can
effectively provide us with a fail safe method of ensuring that code is run within
an if() structure before the program exits the structure. For example if we wanted
our sketch to tell us whether the mouse is specifically in the upper or lower half of
the Display Window we would modify the previous sketch to look like this:
Branching 125
An Introduction To Programming With Processing
void setup() {
size(300,300);
}
void draw() {
if( mouseY >= height/2){
println("Mouse is in the bottom half of the sketch");
println(mouseY);
}else{
println("Mouse is in the upper half of the sketch");
println(mouseY);
}
println("Mouse could be in either half");
}
Notice that as soon as you start the sketch and if your mouse is not hovering over
the Display Window, Processing will read the mouseX and mouseY system
variables as being 0 and 0. As a result the first branch of the if() structure is
skipped because the conditional evaluates to false (0 is less than 150) and the
second branch is immediately run, that being the else code block. Once you start
moving your mouse around in the Display Window you will see the println()
statements execute depending on which half of the Display Window the mouse is
in.
Branching 126
An Introduction To Programming With Processing
if(condition){
...statements
}else
if(condition){
..statements
}else{
...statements
}
As you can see nesting if else constructs within each other is pretty easy, but
when typed in the previous format they can be a bit difficult to read. As a result
they are generally written in the following format, remember that because
Processing is a free form programming language you are free to format the
statements as you wish, however certain formats might be easier to read than
others, for example the traditional format for an if else if else construct follows:
if (condition){
...statements
}else if(condition){
...statements
}else{
...statements
}
There are no limits to how many if else structures you can nest within an if else
construct, but bear in mind that nesting too many if else structures within others
can make it difficult to follow and keep track of what is going on in your code.
Processing and many other higher level languages have structures such as
switch() that might serve your purposes better, if you want your program to be
able to branch between a possibility of three or more control structures.
Logical Operators
Logical operators allow us to to extend our conditionals by creating a means of
comparing one relational expression with another. They come in the format of:
Branching 127
An Introduction To Programming With Processing
These operators can be used within a conditional to return a value of true or false
based on the context in which they are used, for example logical AND will return
a value of true only when both expressions on either side of it evaluates to true
and can be used as in the following examples:
logical OR returns a value of true if at least one of the expressions on either side
of it is true, for example:
if (!(x>1))
...
//can also be expressed as,
if (x<=1)
...
However the logical NOT operator can be useful, for toggling the boolean value
of an expression when that expression is required in several places in a certain
state and only once in the opposite state. For example:
Branching 128
An Introduction To Programming With Processing
boolean x = true;
ellipseMode(CORNER);
if (x) {
rect(25, 25, 50, 50);
}else{
ellipse(25, 25, 50, 50);
}
if (!x) {
rect(25, 25, 50, 50);
}else{
ellipse(25, 25, 50, 50);
}
println(x); //x still has the value of true associated with it
Now that we have an understanding of the context in which branching exists, the
following example of slider.pde will demonstrate a usage for it amongst other
programming features within a typical sketch.
Branching 129
An Introduction To Programming With Processing
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.
S lider Button
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
}
void draw(){
..statements defining draw() structure
}
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.
Datatyping a function
The first line of a function definition must be used to declare the function, and as
you are aware declaration in Processing means giving the function (variable or
other entity) a name and datatyping it. Some programming languages might refer
to the first line of a user defined function as a function header. Naming a function
is a very similar process to that of naming a variable as you will see in the
following code fragment forming the first line of our user defined function:
boolean overButton()
The parenthesis following the function name are used to contain declarative
parameters. These parameters will be defined within the function body, in our
case this function does not accept any parameters so we leave the parenthesis
empty. As you have been using functions that accept parameters user defined
functions can also be created to accept parameters that you define.
In comparison to setup() or draw() our function is being datatyped as a boolean,
all functions must return a datatype or use the void keyword like setup() and
draw(). As void does not return any data it is subsequently not classed as a
datatype within Processing (and traditionally in C/C++) but as a structural
element, this differs to other higher level languages (some of which are closely
tied to Processing such as Arduino) that do consider void as a datatype.
Nonetheless our function is going to return data of type boolean, which will be
used to determine whether the user's mouse is over the slider button or not.
Determining a range
In order to calculate whether the mouse is over the slider button or not we will
have to determine the range in which the slider button is located. We already
know where the slider button is in terms of X and Y because these are values
determined by parameters if the image() function used to draw the image. We can
use this information, along with the width and height object variables to work out
the slider button's range.
Determining the range of the button as the area covered in diagonal stripes.
In the previous image if A and B where variables the image() function to render
the slider button could be rewritten as:
image(sliderFd, A, B);
int buttonXPos;
Eventually we'd like to replace the X parameter for the image() function rendering
sliderFd in the X position determined by buttonXPos. As we are aware what the
variable buttonXPos is going to represent we're going to set it up with a
temporary value within our sketch to test our user defined function. So we're
going to add a temporary statement for testing and debugging purposes to the
draw() function which assigns a value to buttonXPos and then changes the
image() function of which sliderFd is associated with to reflect this assignment
statement. Once the buttonXPos variable is declared in global space, you can edit
the draw() structure to include the first command and update the appropriate
image command:
...
//temp for debugging
buttonXPos = width/2 - sliderBk.width/2;
...
image(sliderFd,buttonXPos ,margin);
Once we are sure that our function is working we will return to the temporary
statement that we adding for debugging and testing purposes and include the
buttonXPos variable assignment statement within an if() structure.
We are now ready to proceed with setting up our function. To briefly recap our
function has been declared as type boolean and given the name overButton(),
setting up the range will occur within a conditional consisting of multiple
comparisons and will look like this:
boolean overButton(){
if (mouseX >= buttonXPos && mouseX <= buttonXPos +
sliderFd.width && mouseY >= margin && mouseY <= margin +
sliderFd.height)
{
Although this conditional might look a little confusing at first, when broken down
into the four distinctive elements that it is comprised of it tends too make a lot
more sense. So lets have a look at what each comparison is calculating and how
these four different expressions put together will either return a value of true or
false, which will ultimately determine whether the following code block of the if()
structure is run or not.
This checks if the mouse is anywhere in the highlighted area of the following
image. If the mouse is then a value of true is returned and Processing will
continue to evaluate the rest of the conditional.
Once again this comparison is only evaluated if the previous comparison was true
because both comparisons are separated with &&. This comparison returns a
value of true only if the mouse is below the top of the slider button.
Another && precedes this comparison which will finally only return true and
determine if the entire compound conditional evaluates to true if the mouse's Y
value is less than the bottom of the slider button.
Now that we have the range setup we're going to tell Processing what to do when
the conditional evaluates to true or false.
boolean mouseOverButton;
Now we'll use this variable in our user defined function to return a boolean back
to the main program.
boolean overButton(){
if (mouseX >= buttonXPos && mouseX <= buttonXPos +
sliderFd.width && mouseY >= margin && mouseY <= margin +
sliderFd.height){
mouseOverButton = true;
cursor(HAND);
//Returns true back to main program
return mouseOverButton;
}else{
mouseOverButton = false;
cursor(ARROW);
//Returns false back to main program
return mouseOverButton;
}
}
Returning data back to the main program is actually really easy and is simply a
means of using the return keyword followed by the data you want to return which
must match the same type as that of the function. Basically, our function is
checking in the conditional if the mouse is within the range defined therein, if it is
then it sets the mouseOverButton variable which has been declared in global
space and datatyped as boolean to true with a simple assignment statement. We
then use the cursor() function to change the mouse icon from the default ARROW
to a HAND. This function also accepts parameters CROSS, MOVE, TEXT, WAIT
and can also accept a PImage as a parameter. Now when the cursor is over the
slider button it will change to a hand icon, giving the user a visual clue that they
are hovering over a clickable element. If we where to leave the function at this
point and not add an else structure, Processing would give us an error when trying
to compile this sketch. This is because every user defined function must return
data of it's specific type, meaning that in our case when the mouse is not over the
slider button and we did not have an else structure capturing this possibility at that
point our function would not be returning any data back to the main program.
This is why it is important that we add an else structure to this if() structure, so
that we can set the mouseOverButton variable to false, and subsequently return
the correct data back to the main program. This also gives us an opportunity to
change the cursor back to an arrow, indicating to the user that their mouse is no
longer over a clickable element.
Our function declaration and definition is now complete and we can use it in our
main program. Calls to user defined functions are as easy as calls to Processing's
API functions. Add the following statement to the draw() structure.
overButton();
Now Processing will run the function at every frame and set the
mouseOverButton variable within the overButton() function to true or false
appropriately, update the cursor and return the correct value datatyped back to our
main program, which we will soon put to use.
boolean buttonDrag;
void mouseDragged(){
if (mouseOverButton){
buttonDrag = true;
}
}
This simple function is run once the user clicks a mouse button and starts to drag,
then the function will check if the mouse is over the slider button as
mouseOverButton is returned to our main program via our overButton() user
defined function. If the mouse is over the slider button and the user has clicked
and dragged in the Display Window, then we can safely conclude that the user is
trying to click and drag the slider button. As a result we set the buttonDrag
variable to true. Now we can use this variable in our main program to update the
position of the button slider in the draw() structure.
Firstly we will start by commenting out the following statement as it is no longer
needed for testing, as we are ready to implement the actual code:
if(buttonDrag){
buttonXPos = mouseX - sliderFd.width/2;
}
This if() statement will form the basis of the program's control flow used to
control the movement of the slider button. If you were to run the script at this
stage you might notice that although you can now click and drag the button, there
are a few new issues that need tending to:
1. The slider button now starts at the 0 X position, and subsequently needs to
be constrained to the region that the sliderBd object covers.
2. When the button is dragged it can be dragged well beyond the sliderBd
region, so this operation also requires constraints.
3. Once the button is released it continues to follow the mouse.
As you can see the slider button has gone too far past the right edge of the
sliderBd image, subtracting the sliderFd's width from the right edge of sliderBd
will solve this.
Modify the if() statement to include the following else clause:
else{
buttonXPos = constrain(buttonXPos, width/2 -
sliderBk.width/2, width/2 + sliderBk.width/2 -
sliderFd.width);
}
The minimum value for the constrain() function is the center of the Display
Window minus half of the width of the slider background image (sliderBd).
The maximum value for the constrain function is the center of the Display
Window plus half of the width of the slider background image (sliderBd) minus
sliderFd's width.
The values that will be used in the constrain() function to determine the slider button's
draggable area
With this information in mind finishing the if() structure is just a matter of copy
and paste with one minor modification. Although the modification is minor it's
addition to the sketch will be what finally gives the user the results they were
looking for. The value we are constraining in this case is going to be mouseX. The
following if() statement demonstrates this:
if(buttonDrag){
buttonXPos = constrain(mouseX - sliderFd.width/2, width/2 -
sliderBk.width/2, width/2 + sliderBk.width/2 -
sliderFd.width);
}else{
buttonXPos = constrain(buttonXPos, width/2 -
sliderBk.width/2, width/2 + sliderBk.width/2 -
sliderFd.width);
}
The mouseReleased() function is pretty simple, all it does is run the function once
every time the mouse button is released in the Display Window. In our case the
function will check if buttonDrag is set to true, if it is then we can be sure that the
slider button is being dragged. As the function is run when the mouse has been
released, we can be certain that the user no longer wants to drag the button, as a
result the function then sets the buttonDrag variable back to false. This means that
the conditional using this variable in the draw() structure will evaluate to false, so
that mouseX will no longer be used to determine the value of buttonXPos. The
mouseReleased() function should look like the following code listing:
void mouseReleased(){
if(buttonDrag){
buttonDrag = false;
}
}
Mapping Values
Now that we have the slider moving correctly we can use the values of
buttonXPos and map them to any values that we choose, for example we could
map the range of values that buttonXPos returns to the amount we would like an
image to scale on it's X axis, how far or fast an image moves across the screen, or
just about anything that can accept a range of values as an input. We're going to
map the buttonXPos range to the tint of an image, making the image appear to
fade in from black. Mapping one range of values to another is done with the
map() function. The map() function accepts parameters in the following format:
The parameter mValue is the value that you will be mapping to the new range, in
our case this is buttonXPos. The parameters oldLow and oldHigh are the
minimum and maximum values of the range you would like to map to the new
range, in other words these values are the furtherest left we can drag the slider
button (oldLow) or expressed within the context of our program:
width/2 - sliderBk.width/2
The oldHigh value is the maximum value of the original range, and in the context
of our program:
You might have noticed, by now that these two values are the exact expressions
we used to determine the minimum and maximum values for the constrain()
function assigned to buttonXPos, this is no coincidence. These two values
determined the maximum range of the sliders movement and this is the exact
range we are mapping to a new range. The new range will be represented by the
two parameters newLow and newHigh and in our case these are two simple
numbers 0 and 255 respectively. As we are mapping the slider button's movement
to the tinting of an image via the tint() function we will be using the single value
that is returned from the map() function which will be between the newLow and
newHigh parameters in order to create the impression of the image fading in from
black. In the context of the actual values, 0 to 255 represent the range of values
that the tint() function uses to tint the image with various levels of gray. For
example the value 0 has the effect of making an image appear as fully gray (or
black) and 255 has the effect of making an image appear with no gray and no
black tint.
With the slider button's values converted to the correct range of values, all that is
left to do is link the new values to the image. In order to do this we will start by
declaring a new variable in the global scope. Add the following variable to your
sketch:
float imgTint;
As you can see this variable is datatyped as float, this is because the map()
function returns floats. Remember that this does not mean that the parameters
themselves have to be floats, nonetheless it is worth noting that in order to
perform true division if the parameters you supply Processing with for the map()
function are of type int Processing will typecast them to floats for the purpose of
the map() function. What this means is that once those values have been evaluated
by the map() function, the answer will be returned as a float. Since this is the
variable that we will assign the return value of the map() function to, and as just
stated this value will be a float it makes sense to datatype this variable as a float.
If we try to use another datatype, such as an int, we will get a type miss-match
error.
With our new variable, imgTint, we will use assignment in the if() structure we
created earlier within the draw() function, to determine it's value as the slider is
dragged. Add the following statement to the if() structure, directly under the
assignment statement for buttonXPos:
In the previous statement we use the map() function to determine the value of the
imgTint variable. Now that we have a place to store this value we can associate it
with the tint() function. Which we will do before the image() function to render
img. We do this by adding the following statement:
tint(imgTint);
This statement must precede the statement telling Processing to render the img
object. If you were to run the sketch at this point you might notice that everything
is working, but not quite as expected. The answer to this problem is order. If you
look at the current sketch it runs like so.
draw()
end draw()
Although this method might be somewhat functional, it's not very intuitive. What
this means is that when the user drags the slider the user should be able to see the
results of their actions in realtime (another way of saying immediately or
something very close to that) and when the user releases the mouse button, the
values should stay as the user set them and not jump around to seemingly
incorrect values. As a result we need to make sure that the images are being
rendered for all possible situations using the correct values, which in our case is
draw()
How this new structure is implemented in our program will follow this pattern:
This is the order within the first control structure of the if else structure the second
control structure will follow the same order. For a full listing of the code it's best
to look at the sketch itself which is named slider.pde.
Finally to end off the example I've added another image to the sketch which I've
associated with the object called star and mapped it's Y movement to the slider.
Like I said the mapping of one value with a range to another range can lead to an
infinite array of possibilities, and is largely the premise on which more complex
applications are built upon. Why not try your own custom mappings you'll soon
come to realize that with this simple function, the sky is definitely not the limit.
Some of the shapes that can be created with the mystery shape maker
Arrays
Arrays are used to store data, but unlike the other data storage types we have been
using (such as variables) arrays have the ability to store more than just a single
value. Arrays can store multiple values of the same datatype in much the same
way that an ordinary list (such as a grocery list) consists of multiple elements
related to the topic of the list. In fact, this analogy is so true to the nature of arrays
that every component making up an array is referred to as an element. This
analogy can be extended ever further, as with a list each element in that list can
have a number associated with it, in an array each element has a number
associated with it too. We refer to these numbers as indices (in plural) or simply
an index number in singular form.
Arrays 150
An Introduction To Programming With Processing
An everyday list:
An Array in Processing:
Index values for arrays start at 0 and can go as high as the number of elements in
an array minus 1, for example if an array has 15 elements, the index value of the
last element in the array is 14.
Element No.2
has an index
value of 1
Arrays 151
An Introduction To Programming With Processing
We can also use an array in a program in a similar way that we would use a
variable, for example:
Index Number 0 1 2
Using an array in this format can be very similar to the way in which variables are
used, and the comparison can extend further than the previous expressional
example into initialization and assignment. As you can see in the previous
example the array was initialized through both declaration and assignment in one
statement in a similar way that variables use initialization, in the first line of the
previous example.
As arrays have multiple elements assigning values to the elements of an array
cannot be through a simple reference to the arrays name, as Processing would not
know which element is to inherit the value on the right of the assignment operator.
As a result the process of assigning values to elements of an array after
declaration or initialization must also include the index number of the element
you wish to assign a value to, on the left hand side of the assignment operator. For
example:
With all the similarities between variables and array elements you might be
wondering what the purpose of an array is? Unlike variables storing one value
followed by another value of the same type within the same variable, the array
does not have to replace the previous value in order to store a new value. For
example, what if I wanted to store the X position of the mouse when the user
clicks in the Display Window. This would be easy enough, I'd just create a
variable and assign it the value of the mouseX system variable. This is fine if all I
need to know is the current X position of the mouse when the user clicks.
Arrays 152
An Introduction To Programming With Processing
However, what if I need to know the previous X coordinate of the mouse, or the
one before that and so on. Retrieving this information with a simple variable
would not be possible because each time the user clicks the mouse the current
value of mouseX replaces the previous value.
With arrays this information becomes accessible to us. By using the elements of
an array we can store the first X coordinate of the mouse at index 0, the second
coordinate at index 1, the third at index 2 and so on. We can store all of this
information without having to create a new variable for each value, which would
make keeping track of all the variables very confusing and difficult. By using an
array we can store all the data related to the X coordinates of the mouse under a
single entity. Lets have a look at an example of this in a sketch.
Arrays 153
An Introduction To Programming With Processing
void mouseClicked(){
if(clicks<4){ //check that the user has not
//clicked more than 4 times.
myArray[clicks] = mouseX; //Sets the mouse X coord to
//the index number in an
//array that matches the
//amount of times the user
//has clicked.
println(myArray[0]); //print the value of the
//first x coordinate
println(myArray[1]); //print the value of the
//second x coordinate
println(myArray[2]); //print the value of the
//third x coordinate
println(myArray[3]); //print the value of the last
//x coordinate
clicks++; //increment the click value
//by 1
}
}
Program Notes
In the first line of the sketch we declare an array with four elements. When using
this technique to create a new array we must indicate the datatype of the array and
use brackets[] to tell Processing that we are about to create an array (and not just
simply declare a variable). We must then name the array and use the assignment
operator to populate the array with the new elements (which do not have values
assigned to them as of yet). We do this by use of the new keyword, followed by
the datatype of the new elements which must match the array's datatype and
finally, brackets indicating how many of these new elements will populate the
array.
Arrays 154
An Introduction To Programming With Processing
Next the variable clicks is declared, this variable will be used to count how many
times the user has clicked in the Display Window. The draw() function is then
initiated in order to keep the program running, and prevent the sketch from
turning to static mode.
The mouseClicked() function consists of a simple if() statement. The conditional
of the if() statement is executed when the user clicks a mouse button within the
Display Window. The conditional checks if the clicks variable is less than four,
which is the amount of elements in the array. If this is the first time the mouse has
been clicked, clicks will equal a value of 0 as it has not been assigned a value
through initialization, it has only been declared. As a result the following
statement is run:
myArray[clicks] = mouseX;
This is the statement that assigns a value to each element in the array. If this is the
first time clicks has been used in the comparison it will also be the first time the
user has clicked in the Display Window and therefore clicks will have a value of
0, as a result the preceding statement can be translated and explicitly expressed
as:
myArray[0] = mouseX;
This means the element with the index number 0 is assigned the value of
whatever the mouse's X coordinate was at the time the user clicked a mouse
button. The next four println() statements print the value of each of the four
elements in the array. As only the first element has a value at this point in time, it
will be the only element that is non-zero until the user clicks again (up to three
more times).
clicks++;
Arrays 155
An Introduction To Programming With Processing
Writing this statement without the increment operator would look like this:
clicks = clicks + 1;
As you can see using an increment operator can make your code easier to read
and save you some keystrokes.
The decrement operator (which is two minus signs --) works in a similar way to
the increment operator except that it subtracts 1 from the variable's current value
before reassigning the new value back to the original variable.
The result of this statement is that the clicks variable will now be one more than
it's previous value by the time the program repeats. For example the clicks
variable was initially 0, but at the end of the if() statement the clicks variable will
have a value of 1. This is important because the next time the user clicks a mouse
button the comparison will be working with different values. For example on the
first click the comparison can be expressed as (0<4), on the second click the
comparison can be expressed as (1<4) and so on. This continues until after the
fourth click, as the fourth click will equate to (3<4). At the end of the if()
statement on the fourth click the variable clicks is incremented to 4, this means on
the fifth click the conditional can be expressed as (4<4), this is false and the if()
statement is no longer executed.
If we did not include the if() statement which serves the main purpose of counting
the amount of clicks, the sketch would still have run but on the fifth click we
would be trying to assign a value to an element in the array that does not exist, as
a result Processing would have given us an Array Index Out of Bounds
exception. You cannot add elements to an array like this, there is however a
method of getting around this restriction, which we will have a look at in our
mystery shape maker sketch.
It's worth noting the print out that appears in the text area every time the user's
mouse is clicked for the first four times within the Display Window. What is most
notable about this, is that the current position of the users mouse's X position is
printed along with all the other X positions that the user's mouse was in when the
user clicked up to three more times previously in the Display Window. All of
these values are now assigned to elements in an array and we now have the option
to use those values in a more complex program, by accessing them through their
index numbers.
Arrays 156
An Introduction To Programming With Processing
First we are going to start by declaring and/or initializing the following global
variables:
Arrays 157
An Introduction To Programming With Processing
The points variable will be a number that we can change before running the
sketch so that we can make our shape consist of as many or few points as we like,
this number should not be negative.
The clicks variable will be used to store the amount of times the user has clicked
in the Display Window, it will also be an operand within the conditional that
populates the array.
Then we move onto declaring the arrays that will store the X and Y coordinates of
the user's clicks. Notice how the number of elements in this array has been
defined as the variable points. As points is of type int it can be placed within the
array's declaration brackets. The preceding statement relating to the declaration of
the numx array can be translated and explicitly expressed as:
It is worth noting that in the actual sketch we have not used a numerical constant
but rather a variable to determine how many elements will populate the array, this
is because if we decided to use more or less points making up the mystery shape,
it would be easier to implement this change by means of modifying the
initialization statement for the points variable rather than having to change
numerical values scattered throughout the sketch, such as in a statement
resembling the previous example.
The final variable we have created within the global space is called randCol and
has been datatyped as color. The color datatype is used to store color values, in
our case we are using the format of:
Where color is the keyword used to declare variables of type color, varName is
the name of the variable, and the assignment operator is used to assign a color
value in R (red), G (green) and B (blue) values. RGB values range from 0 to 255
each. Note the expression used for each of the R, G and B values:
random(256)
Arrays 158
An Introduction To Programming With Processing
random(Low, High);
Where Low is the minimum number in the possible range of random numbers and
High is one more than the maximum number in the range. If you assign the
random() function to a float Processing will return random floats.
The next step is to setup the setup() and draw() functions as per usual, then add
the following if() statement to the draw() function:
if (clicks<points){
numx[clicks] = mouseX;
numy[clicks] = mouseY;
}
There's not really anything new about this if() statement, so we'll just run through
it briefly. First the conditional checks if the amount of times the user has clicked
is less than the amount of elements in the arrays. Using this method creates an
error catching scenario, as opposed to if the user was allowed to click repeatedly
beyond the number of elements in the arrays which would result in the amount of
elements that the program would attempt to store in the arrays exceeding the
amount of elements that can be stored in the arrays. This would cause an Array
Index Out of Bounds exception.
We can then use the clicks variable in the if() structure to determine which
element in the array is assigned the corresponding mouse X or mouse Y value.
void mouseClicked(){
clicks++;
}
Arrays 159
An Introduction To Programming With Processing
Iterations
Processing has two main types of iterations the for() structure and the while()
structure. The while() structure can be thought of as a simplified version of the
for() structure. We will be focusing on the for structure.
The for() structure is used to iterate a value, that is to change a value by means of
a recurring pattern. for() structures are also referred to as for() loops in some
programming languages, whatever you call them they usually will be structured
according to the following protocol.
The main differences that separate one languages implementation of for loops
from that of another, will generally not be anything structural but rather syntactic.
For example some programming languages use comma's to separate init, test and
update Processing uses a semi-colon implying that each is a statement on it's own.
Iterations 160
An Introduction To Programming With Processing
The previous example used a for() loop iteration to count from 0 to 9, lets have a
look at how it did this. First we need to use the for keyword to indicate to
Processing that we are about to create a for() loop iteration. A for loop iteration
relies on three statements init, test and update within parenthesis so that they are
specifically associated with the for() loop iteration structure. As you can see the
three statements are separated with semi-colons, this is unique to a for() loop
iteration in Processing and clearly differentiates these statements from being
parameters.
As init is used to initialize a variable within the for() loop iteration structure, the
scope of this variable is limited to the for() structure. What this means is that the
variable i in our previous example will not be accessible in other places within
our sketch and only be accessible within the for() structure. As a result this
variable name is usually i or n and not very descriptive. It is actually more
distracting to call a variable initialized within a for() loop anything other than i or
n (some people might also use x but this can be a bit distracting from variables
that use x to denote a position on the X axis). It is therefore advisable to not use a
variable name that is any more descriptive than a single character such as i or n.
Once we have the variable initialized we can then use it in a test, if the test
evaluates to true the code within the for structure will be run. It is important to
note that if the test evaluates to true the code within the for structure is run, and
only after this code is run is the update statement executed.
Iterations 161
An Introduction To Programming With Processing
The update statement in our example is an incremental statement, which adds one
to the variable from init subsequently increasing the value of the variable with
each iteration. On the tenth occasion that the loop repeats the variable is
incremented to a value of 10, so when the conditional evaluates the test (10<10)
will return false. This will break the loop and stop it from being repeated.
beginShape();
endShape();
Using this method we can let the user decide what the shape should look like. If
you recall beginShape() and endShape() require the vertex() function between
them. The difference with this sketch is that we have nested the vertex() function
inside a for() loop iteration. The program will loop through the vertex() function
as many times as the variable initialized within the for() loop called i, is less than
the points variable declared earlier in global space. What this translates to is the
the vertex() function will be run six times and on the seventh attempt to run the
for() loop the conditional of the for() loop will evaluate to false as i will be equal
to points (i == points) causing the loop to break and running the rest of the code
that follows the for() loop structure, which would be the endShape() function.
This creates a shape with six vertices (according to our example) in the positions
that the user specified by clicking in the Display Window. The coordinates of
these vertices are determined by extracting the values associated with each
element in the arrays numx[] and numy[], as the index number of each element is
referenced with the for() loop variable i. A full listing of the mystery shape
program follows:
Iterations 162
An Introduction To Programming With Processing
void setup() {
size(300,300);
smooth();
}
void draw() {
background(100);
if (clicks<points){
numx[clicks] = mouseX;
numy[clicks] = mouseY;
}else{
fill(randCol);
beginShape();
for(int tempx = 0; tempx < points; tempx++){
vertex(numx[tempx], numy[tempx]);
}
endShape();
}
}
void mouseClicked(){
clicks++;
}
Iterations 163
An Introduction To Programming With Processing
Transforms
Processing provides a convenient method for animating and interacting with
components of a sketch that are transformed in various ways. The term transform
when used in this context generally refers to any one of, or combinations of the
following three actions:
Translation is simply another way of saying moving but as the term moving can
tend to be somewhat ambiguous when you consider that rotation also involves
movement and so too does scaling (when you consider that the components
making up an object such as it's vertices move when the object is scaled), we
prefer to use the term translation as it specifically refers to positional data in terms
of X, Y and Z of an entity. The function for applying translations within a sketch
is translate() and it accepts parameters in the form of X and Y with an optional
third parameter for Z (which we will discuss later) when using a 3D renderer.
Scaling is a transform accessed with the scale() function and it accepts a single
float value or two floats that relate to X and Y or three floats that relate to X, Y
and Z for 3D sketches. Scaling creates the impression of making things appear to
be bigger or smaller within a sketch.
Let's have a look at an example of a static sketch using the translate() function.
size(400,60);
rect(0,0,50,50);
translate(width/2, 0);
fill(255,0,0);
rect(0,0,50,50);
Transforms 164
An Introduction To Programming With Processing
In the first line of the sketch, we simply defined the size of the window in which
the sketch is running as being very long horizontally and short, vertically. In the
following line we drew a rectangle at the origin, and because the rectMode()
function by default is set to CORNER, the rectangle sits perfectly flush with the
top left hand corner of the Display Window. We then used the translate() function
with a modification to the X parameter, and as you would expect the next time we
draw a rectangle (after changing the fill color) it's position on the X axis reflects
the translation we previously applied. However, if you have a look at the X
parameter for the rect() function you will notice that things are a bit different to
what you might expect as the X parameter reads 0 whereas the rectangle is drawn
close to the center of the Display Window and to the right of the previously drawn
rectangle.
Shouldn't the rectangle be drawn at the origin if it's X parameter is 0?
If you were thinking something along those lines then you are absolutely correct
and in fact the rectangle is being drawn at the origin, the main difference here is
that we have used the translate() function to move the origin, not of the rectangle
but of the entire coordinate system. Let's have a look at how this happens.
Transforms 165
An Introduction To Programming With Processing
An object drawn on graph paper does not move but the graph paper does
This is the purpose of transforms in Processing, they move the coordinate system
for us. We can still move the components of a sketch through their own positional
parameters, which we usually supply in terms of X and Y data for parameters of a
function, but bear in mind that mixing translation techniques like this will result
in the component appearing to have moved by the sum of the two data sets. To
put this in another way, the distance the entity would appear to have moved would
be the sum of the translate() function plus the X, Y and Z parameters of the
function to draw the entity.
Transforms 166
An Introduction To Programming With Processing
first we're going to adapt the sketch for active mode by including the drawing of
the face within the draw() structure:
void setup(){
size(640,480);
}
void draw(){
background(127);
//Drawing the face
fill(191,233,255);
ellipse(width/2, height/2, 100, 100);
//smile
noFill();
stroke(255,0,0);
arc(width/2, height/2 + 10, 50, 50, 0, PI);
//eyes
stroke(0,0,0);
fill(0,255,0);
ellipse(width/2-15, height/2-15, 20, 30);
ellipse(width/2+15, height/2-15, 20, 30);
}
The smile static sketch revisited and modified to fit an active mode sketch
As you can see nothing surprising about the sketch, but what if we wanted to
animate the smiley face by moving it across the Display Window, or attaching it
to the mouse?
This could become quite a cumbersome task as every component of the smiley
face that has X and Y parameters would have to be updated for every frame. To
Transforms 167
An Introduction To Programming With Processing
give you an indication of how much extra work this is here's a look at what sort of
changes in the sketch would need to be implemented if we wanted to attach the
smiley face to the mouse:
void setup(){
size(640,480);
}
void draw(){
background(127);
//Drawing the face
fill(191,233,255);
ellipse(mouseX, mouseY, 100, 100);
//smile
noFill();
stroke(255,0,0);
arc(mouseX, mouseY + 10, 50, 50, 0, PI);
//eyes
stroke(0,0,0);
fill(0,255,0);
ellipse(mouseX-15, mouseY-15, 20, 30);
ellipse(mouseX+15, mouseY-15, 20, 30);
}
As you can see almost every second line of code had to be updated within the
draw() structure. Now imagine if you had a whole character you wanted to draw
and how much additional work something like that could amount to?
With transforms making a change like this to your code is easy:
Transforms 168
An Introduction To Programming With Processing
void setup(){
size(640,480);
}
void draw(){
background(127);
translate(mouseX-width/2, mouseY-height/2); //transform
//Drawing the face
fill(191,233,255);
ellipse(width/2, height/2, 100, 100);
//smile
noFill();
stroke(255,0,0);
arc(width/2, height/2 + 10, 50, 50, 0, PI);
//eyes
stroke(0,0,0);
fill(0,255,0);
ellipse(width/2-15, height/2-15, 20, 30);
ellipse(width/2+15, height/2-15, 20, 30);
}
Notice that only one additional line of code was needed to accommodate for this
update, the rest of the code remained unchanged.
The additional statement is:
translate(mouseX-width/2, mouseY-height/2);
This statement simply uses the translate() function, with two parameters relating
to X and Y. The X parameter tells Processing to move the entire coordinate
system to the position of the mouse's X value then to subtract half the width of the
Display Window from this value. The reason we need to subtract half the width of
the Display Window from the mouseX system variable is because our entire
sketch was originally made to draw the smiley face in the center of the Display
Window.
Transforms 169
An Introduction To Programming With Processing
As the coordinate system is transformed, it's origin can exist within the boundaries of the
Display Window or outside of these boundaries.
Transforms 170
An Introduction To Programming With Processing
Main Cart
Background wheel
Foreground wheel
The function pushMatrix() when called before a transform is executed will store
the current state of the coordinate system. This means that if you call a
pushMatrix() function at the start of the draw() structure it will remember the
default position of the coordinate system (pre-transform). You are then free to
apply transforms to the particular component of the sketch that you would like to
scale, rotate and/or translate. Once you are satisfied with the transforms you have
performed you can then render the component, for example by means of a call to
the image() function. Running the popMatrix() function at this point will then be
necessary to restore the coordinate system back to the state it was in when the
previous pushMatrix() was called. You can then proceed to run more
pushMatrix(), transforms, render, popMatrix() combinations on other components
of the sketch which will remain unaffected by the previous transforms. Using
pushMatrix() and popMatrix() in this way allows us to apply multiple transforms
to a component of a sketch ,and not have those transforms effect other
components.
Transforms 171
An Introduction To Programming With Processing
Lets have a look at how to use this technique to make the cart move across the
Display Window and have the wheels of the cart rotate accordingly.
We'll start as always with a template of what we want the final result should look
like:
PImage cart;
PImage wheelFd;
PImage wheelBd;
PImage grass;
void setup(){
size(800,600);
cart = loadImage("cartMain.png");
wheelFd = loadImage("wheelFd.png");
wheelBd = loadImage("wheelBd.png");
grass = loadImage("grassLessBlur.jpg");
}
void draw(){
background(grass);
imageMode(CENTER);
image(wheelBd, -75, 25);
image(cart, 0, 0);
image(wheelFd, -95, 35);
//println(mouseY);
}
Transforms 172
An Introduction To Programming With Processing
Out of interest you might be wondering how the X and Y parameters of the
image() functions which render the wheels relative to the position of the cart were
determined. If you look at the last line you'll see a println() function that has been
commented out. By replacing the image() function's X and Y parameters with
mouseX then mouseY, respectively and one at a time, you can run the sketch with
the image of the wheel attached to either the mouse's X or Y position. Place the
wheel in the position it should be in, the println() function (when uncommented)
will print out the position of your mouse, note this value and replace it with the
mouseX or mouseY system variable in the wheel's image() function's
corresponding X or Y parameter.
As the imageMode() function is set to CENTER in this example you will have to
move the main cart image to a location that is not the origin, so that the left and
top halves of the image is not obscured by the boundaries of the Display Window.
You will then need to subtract the value that you added to the X and Y parameters
of the image() function to draw the cart from the respective values printed in the
Text Area/debugging console that will be used to place the wheels in the correct
locations.
Now that we have a template from which to start let's set up a temporary system
that allows the cart to trail after the mouse with a bit of friction, we'll then modify
this code to suit the needs of our sketch. This methodological approach to
sketching by creating a rough idea then refining it will allow us to see results
much sooner in the sketching process, rather than spending a lot of time on a
sketch only to find out when it is almost done that the effect we were trying to
achieve isn't quite working out.
We'll start by adding the following global variables:
float xPos;
float difX;
int drag = 30;
If you recall from the imageScroll.pde sketch, we used the technique of creating
the impression of friction with a similar set of global variables. The only
difference is that in this sketch we are now using the same technique to create the
impression of friction along the X axis. Next we'll need to add the following
statements to the draw() structure after the imageMode() function call:
Transforms 173
An Introduction To Programming With Processing
Since we want the cart to move with relation to the mouse moving past the end of
the cart on the right hand side, we're going to divide the cart.width object variable
by 2 and add it to the position of the cart which is now determined by the center
of the cart object because we have set imageMode() to CENTER. If imageMode()
had been at it's default value of CORNER we would simply use cart.width to
determine the right edge of the cart image.
To temporarily link these statements to the rendering of the cart, directly after the
previous statements add the following code:
pushMatrix();
translate(xPos, 250);
image(wheelBd, -75, 25);
image(cart, 0, 0);
image(wheelFd, -95, 35);
popMatrix();
Notice that we just added three additional statements. The first new statement is
pushMatrix() this tells Processing to store the current transformational data of the
coordinate system in memory, we then use the translate() function to to modify
the coordinate system along the X axis by the value of the variable xPos and the
numerical constant of 250 for the Y parameter. The Y parameter is a numerical
constant simply because it will not change throughout the duration that the sketch
is running. Next we render the images without any changes to their parameters as
all of their positional data remains the same, the transform will create the
impression of movement for us. Then finally we use the popMatrix() function to
restore the transformational data relating to the coordinate system when
pushMatrix() was initially called.
When using pushMatrix() it must always be coupled with popMatrix() or
Processing will complain about there being too many calls to pushMatrix() and
not enough to popMatrix().
Transforms 174
An Introduction To Programming With Processing
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
Transforms 175
An Introduction To Programming With Processing
pushMatrix();
translate(xPos -75 , 250 +25);
rotate(xPos/wheelBd.width);
image(wheelBd, 0, 0);
popMatrix();
image(cart, 0, 0);
image(wheelFd, -95, 35);
As you can see we have removed the image() function that renders the wheelBd
object and placed it between a pushMatrix() and popMatrix() function pair.
Between pushMatrix() and popMatrix() we are free to use translate() and rotate()
to modify the coordinate system before rendering wheelBd with a call to the
image() function. When we originally rendered the three images making up the
cart, we had to offset the back wheel and front wheel so that they we in the correct
places in relation to the position of the main cart. When performing
transformations to an image it's best the have the image transform from the origin.
As a result the X and Y parameters of the image() function used to render the
image should remain at 0, and translations should be used when the image's X and
Y coordinates need to change. This is often the simplest and most logical
approach to working with transforms as it ensures that the image's relative
distance to the origin is not modified which could lead to unexpected results.
Transforms are more predictable when the image() function's parameters are set to 0, 0
for X and Y. Offsets in these parameters will often result in undesirable transformations.
Transforms 176
An Introduction To Programming With Processing
By setting the imageMode() function to CENTER and the X and Y parameters for
the image() function to 0 and 0 we can be assured that the image of the wheel is
going to be rendered with it's center at the origin. This is important because all
transformations are relative to the origin, so that when we rotate the wheel it will
appear to rotate from it's center, this is also due to setting imageMode() to
CENTER and not leaving it at it's default of CORNER which would have resulted
in the wheel appearing to rotate from it's top left hand corner. Bearing this in mind
it is also important that the images that we use for the wheels are square in shape,
in other words the image's width should match the same image's height. This
creates the impression of the wheels being flat on the ground when they start to
rotate. The following statement, is how we remove the image() functions offset
and are still able to match the position of the cart with the mouse's X value:
The xPos variable has not changed in terms of how it is applied in this sketch, the
difference is that the X and Y parameter values that were used in the image()
function to render the wheel have now been added to the translate() function's X
and Y parameters. The rotate() function follows:
rotate(xPos/wheelBd.width);
As the xPos variable changes relative to the distance the cart has moved we can
use the changing properties of this variable to make the wheels rotate. We can
then divide this number by the width of the wheel image which is also the
diameter of the circular shape of the wheel. This expression is based on the
formula for pi which is pi = circumference/diameter, but it is worth noting that the
value returned from our modified expression is not equal to pi, as this would be a
constant value as a result xPos is substituted for the circumference of the circle as
this will yield a value that changes relative to the distance that the cart has
traveled.
We can then render the image at the origin after the coordinate system has been
transformed, so the image appears to have moved because the coordinate system
it is rendered relative to has, by then, been transformed.
image(wheelBd, 0, 0);
Transforms 177
An Introduction To Programming With Processing
Finally use popMatrix() to reset the coordinate system back to it's original
transformations.
We can then continue to render the main cart and foreground wheel using the
same techniques, and not have transformations from one rendering effect another.
In the final cart.pde example I've added a shadow, try to figure out how you
would go about creating this effect before looking at the code. Here's a hint, I've
added four additional images to the sketch.
Transforms 178
An Introduction To Programming With Processing
An Introduction to 3D in Processing
Up until now we have been using Processing to create 2D sketches, that is
sketches with 2 measurable dimensions such as width and height, however
Processing allows us easy access to another dimension that being depth. As width
is usually associated with the X axis and height is usually associated with the Y
axis, depth is commonly associated with the Z axis in Processing.
X Z
In Processing we have access to two 3D renderers, P3D and OpenGL. The P3D
renderer is used mainly for 3D sketches on the web, the OpenGL renderer
requires that the machine running the sketch must have OpenGL acceleration
capabilities. Many modern 3D games (particularly those that are commonly
termed platform independent) and most 3D content creation software uses
OpenGL, as it is not specific to Processing and a set of features that abstract the
details of hardware acceleration for software developers. Using the P3D renderer
in a Processing sketch is easy, as this renderer is simply invoked by adding an
extra parameter to the size() function. For example:
This would cause Processing to use the P3D renderer. Using the OpenGL renderer
is slightly different as this requires that the OpenGL library is first imported:
import processing.opengl.*;
All import statements in a sketch should precede all other statements. You can
then use the OPENGL mode as a third parameter for the size() function.
A typical example of a sketch using OpenGL might look something like this:
import processing.opengl.*;
void setup(){
size(500,500,OPENGL);
}
void draw(){
background(127);
}
3D Primitives
In a similar way that Processing has 2D primitives such as rectangles, ellipses,
lines etc you will also find a set of 3D primitives such as a box and a sphere.
However you are free to use 2D primitives in a 3D sketch, but not 3D Primitives
in a 2D sketch. One of the fundamental differences that separates the way in
which the functions used to render 3D primitives differs from rendering 2D
primitives, is that 3D primitives do not have parameters for X, Y and Z
coordinates. For example the function to draw a box which is box() can accept
three parameters but these parameters relate to width, height and depth.
translate(X, Y);
//or
translate(X, Y ,Z);
Where X, Y and Z would be the numerical values relating to how the coordinate
system is translated. The rotate() function can also be used in the following
context:
rotateX(num);
//or
rotateY(num);
//or
rotateZ(num);
Where num is the numerical value in radians that the coordinate system will be
rotated around the corresponding axis.
Using the box() function and translations here's an example of how we could go
about drawing a simple robot character using OpenGL:
import processing.opengl.*;
void setup(){
size(500,500,OPENGL);
}
void draw(){
background(127);
pushMatrix();
translate(width/2, (height/2)-100);
box(40);
translate(0,80);
scale(0.8,1);
box(80);
translate(70,0);
scale(1,2);
box(30);
translate(-140,0);
box(30);
translate(50,50);
scale(1,1.5);
box(30);
translate(40,0);
box(30);
popMatrix();
}
Creating 3D sketches can be fun, but can also become exceedingly difficult for
the beginner as you will often find that a firm grasp of trigonometry is necessary
and depending on what you are wanting to achieve an understanding of vector
math and matrices might also be a prerequisite. However if you are interested in
learning more about creating 3D sketches the best place to start is to work your
way up from creating 2D sketches, that make use of the transformation matrix and
basic trigonometric functions, then try adapt these ideas to include three
dimensions.
From this description you can see that the results of both programming paradigms
can eventually lead to the same thing, however the process of getting to that result
is what distinguishes one programming paradigm from that of another.
OOP as you are aware relies on classes, from which we instantiate objects. These
objects are the reason why we refer to this programming paradigm as Object
Oriented Programming, and emphasize the Object part. We have already been
using classes that are a part of Processing's API such as PImage from which we
have instantiated object variables which we have given names, and those objects
have inherited various properties and functions (called methods in OOP) from the
classes from which they where instantiated. But what exactly is a class?
The benefit of this is that when multiple objects are instantiated from a single
class they all inherit the methods of that class, this allows you to use certain
methods with one object and certain other methods with another object. Thereby
creating relationships between those objects (and ultimately the data you are
representing with you main program) with other data or API features in ways that
would be very difficult or maybe not practically possible without OOP.
Branch 01
M ethod 1
Branch 02
M ethod 2
Branch 04
Branch 03
One class can result in many different branches.
If you can imagine a class to be like a blueprint then a software object is like a
house made from that blueprint. For example, a class only describes the possible
objects that can be instantiated from it, taking the blueprint analogy further we
cannot live in a blueprint yet it has all the information we need to build a house
which would be something that we use as a functional object. In other words we
use the blueprint to build a house, this is just like instantiating an object from a
class. The class only describes the possibilities of an object that can be
instantiated from it and when we finally do instantiate an object from the class, it
is the object that we use in our main program.
Just like a blueprint describes a structure it does not say anything about how the
structure can be used for example a blueprint can be used to build somebody's
home or the same blueprint can be used to build an office. The purposes the
buildings serve are different, but the structure of the buildings remains the same.
The relationship between classes and software objects is very similar, in that one
object instantiated from class can serve a certain purpose and another object
instantiated from the same class can serve a different purpose, but because both
objects are instantiated from the same class the context in which they are used
will tend to have similarities.
Creating a Class
As previously mentioned a class typically consists of method definitions and
various fields (which we also refer to as member variables) that store information
about the current state of the object instantiated from the class. The term fields
refers to variables that are members of a particular class, and as a result are
encapsulated data that store information about the object itself and are sometimes
referred to in Processing as class data.
The structure of a class looks similar to setup() and draw() and user defined
functions, but since a class is not a function, but may contain many functions
(called methods) it's name is not followed by parenthesis. An object variable
name which we will have a look at shortly, on the other hand is followed by
parenthesis in order to provide a means of communicating with the object's
internal structure.
void setup(){
}
void draw(){
}
class ClassName{
}
As you can see a class definition usually finds it's place at the very end of a
sketch, this is sometimes referred to as an in-line class definition. This simply
emphasizes that the class is directly related to the sketch that it is defined within.
As defining a class at the end of a sketch, after user defined functions, mouse and
keyboard functions etc can create a cluttered looking sketch Processing also
provides us with Tabs in which we can place additional code that will be compiled
with the sketch we are currently working on. You can create a new tab by clicking
the arrow pointing to the right on the far right of the PDE and a fly-off menu
relating to Tabs will appear.
Processing will then ask you to provide a name for the new tab.
Once you supply a unique name and click OK Processing will create a new blank
Tab next to the original Tab. What has actually happened is that Processing has
created a new .pde file that has the name you specified for the Tab that was just
created. This new .pde file resides in the same location as the sketch you are
currently working on, as a result it can access anything in the data folder that you
can access from the code of the original Tab. Although Processing has created
another .pde file this file is only part of a sketch. In a similar way that the original
pde file can be reliant on the contents of the data folder, the new .pde file in the
same location as the original .pde file can also be reliant on the original .pde and
the contents of the data folder. As a result we refer to this collection of elements
(i.e. pde files, txt files, spreadsheets, images and anything else that is in the data
folder used in the sketch) collectively as the contents or components of a sketch.
Using additional Tabs in a sketch provides us with a convenient location for
placing classes. This is useful because it emphasizes the modular design of classes
and clears the main .pde file from becoming over-cluttered with code. Working
with additional tabs is not synonymous to working with multiple sketches.
Multiple tabs are a means of breaking up the current sketch you are working on
into smaller manageable portions of code. As a result when a sketch with multiple
Tabs is run all the code within all the Tabs of that sketch are compiled together.
This means that only one of the Tabs should have setup() and draw() structures,
the other Tabs should be used for class definitions, user defined functions or other
elements of a sketch that exist outside of setup() and draw() structures.
A Button Class
If we wanted to create a class for a button, that we will call Button. This class will
have
fields that will contain certain information about the object instantiated
from the class such as the color of the button, it's size and position.
The class will also have a constructor that will use the class's fields to
create an initial state for the object.
Finally the class will have two methods, one that renders (or displays) the
button and another that tests whether the user's mouse is over the button.
Class Name
In order to give the class a name and let Processing know that we are about to
create a class we must use the class keyword. The name of a class generally starts
with an upper-case character. For example. We'll start by adding a new tab and
calling it Button. In this tab add the following code:
class Button{
}
This is how we create a class and name it in Processing. As you can see the class's
name is Button which starts with an upper-case character, with regards to popular
standardized coding practices.
Fields
When we instantiate an object from this class there are several properties we'd
like this object to inherit from the class, which we will have access to modifying
from the main program but not be able to modify the definitions of these
properties as this will be encapsulated within the Button class. A list of the these
properties follow:
1. the color of the rectangle that will visually represent the button object,
2. the object's X coordinate
3. the object's Y coordinate
4. the object's width
5. the object's height
6. and a name for the object.
As a result we will need at least all six of these properties represented in the
class's fields as variables.
Let's add those fields to the class, inside the braces of the Button class add the
following declarations:
color cB;
float xLocB;
float yLocB;
float xSizeB;
float ySizeB;
String nameB;
As you can see each of these variables will hold the information related to the
previous list of six items.
Constructor
To give you an idea of how these fields (or variables) will be used we need to fast
forward a few steps into the future and take a quick look at what happens when
we instantiate the class. The process of creating an object is actually nothing new
to you, as you have already instantiated objects from the PImage class. One of the
main differences with our Button class is that we will be using it's constructor to
initialize objects instantiated from it, so when we get to doing this we will use an
initialization statement that will in part look like this:
From this partially complete code fragment, Button accepts the parameters the
user has input, which will be assigned to the object's fields (which were inherited
from the class's fields). These parameters will be used to initialize the object
instantiated from the Button class. However at the moment, these variables don't
really do much, or store any information that will help in constructing the button
object. This is why we need a constructor. The constructor is like a method that is
automatically called every time a new object is instantiated from a class.
The main purpose of a constructor is to determine a default state that an object
will exist in as soon as it is instantiated. The constructor is defined below the
fields declaration of a class and our constructor will look like this:
Note that the constructor has the same name as the class, this is a requirement for
using a constructor. When we refer to Button() we are actually referring to the
constructor of the class and not the class itself, which we simply refer to as
Button.
The first line of the constructor tells Processing how the Button class will accept
parameters when a new object is instantiated from it. You might have also noticed
that we have declared six new variables that will temporarily store the values that
the user inputs as parameters when an object is instantiated. These values will
then be assigned to the original member variables which will store the values that
the user has input as parameters when instantiating the button. Using this
approach to designing a constructor ensures that multiple objects can be
instantiated using different parameters with the same constructor.
At this stage in the design of the Button class we have fields declaring the
member variables that will be used to store information about the button, and how
we would like to use those fields to construct the button, but we have not told
processing to actually draw the button to the Display Window so that the user can
see a visual representation of the object and interact with it. This is amongst one
of the many reasons why class's have methods.
Methods
In order to render this visual representation we're going to create a method called
disp() which will create a rectangle from the parameters the programmer inputs
into the Button() constructor when instantiating a new object. Just like user
defined functions methods must use the void keyword if they do not return a
datatype. Here's what our disp() method (short for display method) will look like:
void disp(){
fill(cB);
rect(xLocB, yLocB,xSizeB, ySizeB);
fill(0);
text(nameB, (xSizeB/2)+xLocB-((xSizeB/2)/2),
(ySizeB/2)+yLocB+((ySizeB/2)/2));
}
Creating a method follows the same routine as creating a user defined function.
Our disp() method is really quite simple all it does is accept the cB variable as a
fill() color, draw a rectangle with the positional data from xLocB and yLocB and
finish the rectangle by determining it's size from the xSizeB and ySizeB
variables. The method then goes on to set the fill() to black and render the name
of the button (nameB) in the center of the button.
A class can be as simple or a complex as you want, and since we have all the main
ingredients of our class we're going to return to our main program and have a look
at how to instantiate an object from this class.
Object Instantiation
Object instantiation is not a new concept to you as you have already instantiated
objects from the PImage class. The process of instantiating an object from our
Button class will follow a very similar pattern.
Since we want this object to be accessible from both setup(), draw() and user
defined functions we are going to declare an object variable in the global variable
scope by adding the following declaration outside of both setup() and draw():
Button myButton;
We now have a variable name that we can use to reference the object we just
instantiated from the Button class, this name is myButton.
Within the setup() structure we'll initialize the button, by use of it's constructor:
myButton (the object variable) has inherited the properties of the Button class. If
you were to compare this statement with the modified example of the class's
constructor:
//this is simply for illustrative purposes
Button(cB, xLocB, yLocB, xSizeB, ySizeB, nameB);
You will notice that this button is currently being constructed, but remember that
you will not actually see the button at this stage as all we have done is told
Processing how we would like the button to be constructed we have not told
Processing to actually display the button. The use of the new keyword in the
previous listed assignment statement is followed by the class's constructor which
is why we are using Button() and not Button.
Now that we have told Processing how we would like this new button constructed
let's move onto the draw() structure and actually render the button.
Using an object's method is easy, and is very similar to a function call. The main
difference is that the name of the method is preceded by the name of the object
itself. For example:
myButton.disp();
Add this statement to the draw() structure, and you'll finally see your button.
As you can see when working with OOP the main program looks less cluttered
with code and the majority of the code exists in creating classes. This for many
people creates a more readable interface for designing software, and once you get
the hang of using OOP it can also help to localize problematic code that might be
difficult to locate without a program adopting a modular design.
The completed sketch ButonClassExample.pde also contains another method
called over() which will identify if the users mouse is over the button, if you take
some time to examine the sketch you should be able to add your own
functionality to the button.
A text file imported into a sketch should have the file extension .txt. A word
processor document might have additional formatting and as a result might
produce unexpected results when used in a sketch. For this exercise it is
recommended that you use the file named names.txt in the data folder of the viz05
exercise directory.
Importing a text document into a sketch works in the same way that images are
imported into a sketch. Locate the file to be imported and click and drag it into the
PDE with the sketch you are working on. The PDE will report that a file has been
added successfully to the sketch and if you were to open the sketch's data folder
you will find a copy of the file in there. In order to display the contents of the
names.txt file in the Display Window we will use an array of Strings with each
element of the String array representing a line of characters in the names.txt file.
Lets have a look at how to do this.
In the global variable scope declare a new array of Strings and call it names:
String[] names;
Notice that declaring an array like this does not indicate how many elements will
be in the array. This is because we want Processing to tell us how many elements
are going to make up this array. In other words if we had a text document with
many lines each of which was intended to be an element in an array, instead of
counting the lines in the text document and creating the array like so:
We would use the former method were the number of elements is decided by
Processing and if we wanted to know how many elements Processing has
populated the array with, we would use the array's object variable, length to find
out. We'll have a look at how to do this a bit later.
In setup() we're going to populate the array with the loadStrings() function, which
will accept one parameter that is the name of the text file containing the data that
will be used to populate the array.
names = loadStrings("names.txt");
We now have an array of Strings that we can refer to by it's variable name called
names. This array has several elements each consisting of a String representing
each individual line of the names.txt file.
Finally to display the information contained within the elements of the names
array we will use the text() function within a for loop:
void draw() {
for(int i = 0; i < names.length; i++){
text(names[i], 20, i*20 + 20);
}
}
The array object variable's names.length data stores the amount of elements in the
names array, this information is important for creating the test of a for loop
iteration.
The text() function uses each element in the names array as a data input
parameter, subsequently rendering each line of the text file one after the other and
offset on the Y axis.
Using this technique of loading external data does not have to be confined to
working with the Strings datatype. Typecasting provides a convenient approach to
converting String data into numerical data that can be calculated. Here's an
example of the loadStringsExample.pde file that does just that with a file
containing numbers. It is worth noting that although when you open the
numbers.txt (in the viz05 data directory) file it reveals a list of numbers to you,
when this information is read into our sketch Processing identifies this data as
String data. Which is why we must first typecast each element of the numbers
array with the int() function into an int datatype before we can perform addition
on the data.
String[] names;
String[] numbers;
int tCast;
void setup() {
size(300,300);
smooth();
names = loadStrings("names.txt");
numbers = loadStrings("numbers.txt");
}
void draw() {
background(100);
for(int i = 0; i < names.length; i++){
text(names[i],20,i*20 + 20);
}
for(int i = 0; i < numbers.length; i++){
tCast += int(numbers[i]); //typecasting int(numbers[i])
}
text(tCast,250,20);
}
Attribution
Images
Attribution 200