tabreturn.github.io

Processing.py in Ten Lessons – 01: Hello World

2018-06-12

Covered in this lesson:
Intro to Processing / Colour / Drawing / Variables / Arithmetic-Operators


 
This series of tutorial posts covers Processing.py, with the purpose of introducing non-programmers to the fundamentals of computer programming in a visual context. If you’re an artist, student, designer, researcher, or just somebody who is keen on learning how to code, Processing is a great place to start.

Complete list of Processing.py lessons

I would estimate each lesson to take around 2–4 hours to complete, of which a large part of that time will be spent on the tasks/challenges. The speed at which you get through the lessons is likely to be influenced by your prior experience in relatable areas. That said, it is not a race – enjoy the ride and stop for breaks wherever. Hey, if you’re really feeling inspired, feel free to head off-road!

My thanks go to Åke Jonsson for his feedback on these lessons.

Let’s begin.

Intro to Processing

Processing has been around since the early 2000’s and is comprised of a programming language and an editor for writing and compiling code (IDE). The original programming language component is based on Java, but several other variants have been developed. These include a JavaScript (p5.js), Ruby (JRubyArt), and Python (Processing.py) version.

As somebody teaching programming fundamentals to creative students – who will proceed to take courses in web, game, and interactive design & development – combining Processing with Python provides an ideal learning tool. What’s more, Processing costs nothing, is open source; and runs on multiple platforms, including Windows, Mac, and Linux.

You can think of Processing.py as a kind-of extension of the Python programming language, with all sorts of drawing features and an editor rolled-in. Sometimes it may seem that these notes use the terms “Processing” and “Python” interchangeably. I will usually refer to Processing-exclusive features in the context of “Processing”; and standard Python features in relation with the term Python. If this confuses you, think of them as one and the same! At this point, it is not important that you are able to distinguish Processing from Python, although you will come to understand the differences in time.

Setup Processing

Before you can write any code, you will need to download Processing. Head over to the Processing download page, and download the relevant version (Windows/Mac/Linux):

processing.org/download

Run the application. Note that the default mode is “Java”. As these tutorials cover the Python variant, you’ll need to switch to Python mode.

Click on the down arrow next to “Java” and select Add Mode… . Then, from the Contribution Manager window that appears, choose Python Mode for Processing 3. Finally, click Install.

Adding/installing Python mode.

Once this is complete, you can switch to Python mode.

Switching to Python mode.

You are now ready to write your first line of code! If you would like a demonstration of what can be accomplished, perhaps take a look through the examples (File > Examples…) included with Processing.py. If you are ready to begin writing your own code, move onto the next section.

Use the ▶ button to run any code.

Algorithms

You will encounter the term algorithm frequently from here onward. For these tutorials, the definition of an algorithm is: a set of rules a computer or machine follows to achieve a particular goal. As an example, an algorithm for making a cup of instant coffee will read as follows:

  1. place one heaped teaspoon of instant coffee in a mug;
  2. fill the kettle with water;
  3. switch-on the kettle;
  4. once boiled, add 240 ml boiling water to the mug;
  5. add one level teaspoon of sugar to the mug;
  6. stir the contents of the mug.

However, this set of steps would likely be insufficient for programming a coffee-making robot. Should the sizes of the mugs vary, smaller ones would overflow; and any requests for milk or extra sugar would be ignored. Computers cannot make any assumptions and therefore require explicit and unambiguous direction, and this requires communicating in a language the machine understands – like Python. Learning to write code may be the challenge you currently face, but as you grow more fluent, this will shift more towards mastering algorithmic thinking.

Hello World

Processing refers to programs as “sketches”. Given the visual and artistic nature of what you are likely to produce, it’s a fitting term. Create a new sketch by selecting File > New, or using the associated keyboard shortcut (which is listed alongside the menu entry).

Processing Functions

Type in the following lines of code:

size(500, 500)
print('hello world')

Using File > Save as… save the sketch (to wherever it is you wish to store it) and name it “hello_world”. You will notice that each sketch is saved as a new folder – in this case, a folder named “hello_world”. Within this are two files: hello_world.pyde and sketch.properties.

You may also add other assets to your sketch folders, such as images and fonts – but more on that later.

Hit the ▶ button to execute the code. Better yet, use the associated keyboard shortcut: Ctrl+R for Windows and Linux; or ⌘+R for Mac.

A grey 500×500 pixels display window (left) appears when you run your sketch.

You have used two Processing functions here – size() and print(). Functions consist of a function name followed by an opening and closing paren (or round bracket, if you prefer). Within these brackets, you supply arguments. The size() function takes two arguments: the first represents the width of your sketch; and the second, the height. In this case, the display window is 500 pixels wide by 500 pixels high. For reference purposes, you could write out the size function with the required arguments named according to the values they accept:

size(width, height)

Throughout these notes, you will find functions presented in this manner to explain their operation.

The print() function writes to the Console area – the black rectangle at the bottom of the editor. Print takes a single argument; in this case, the phrase 'hello world'. Because this is text – or, more correctly speaking, string data – it must be wrapped in quotation marks. You may use single- or double-quotes, but be sure to close-off using the same type with which you have opened.

What separates Processing.py code from Python code are the functions. The size() function is Processing-specific. In other words, it will not work outside of the Processing environment. The print() function, on the other hand, is a built-in part of the standard Python programming language. It will, therefore, work in any Processing.py or Python application.

Processing Reference

For a complete list of Processing.py functions and the arguments they require, you can refer to the online reference:

py.processing.org/reference

Note that the reference also includes many standard Python language structures, keywords, and functions – most of which we will cover along with the Processing functions.

Comments

Comments can be used to leave notes to yourself or anybody else editing your code. These come in two types: single- and multi-line. Use a # character for single-line comments, and ''' or """ for multi-line comments. Add some comments to your current sketch to see how they work:

# dimension of the display window in units of pixels
size(500, 500)

print('hello world') # writes hello world to the console area
'''
This is a multi-line comment.
Any code between the opening and closing triple-quotes is ignored.
'''
print('how are you?')

While working through these lessons, add your own comments to remind you of what you have learnt. You can also comment out select lines of code, which can prove useful when debugging.

Whitespace

Python, and by extension Processing.py, is whitespace-sensitive. As an example, add a few space characters at the beginning of the size() line; then run your sketch.

# dimension of the display window in units of pixels
    size(500, 500)

...
Console displaying errors.

Because Python relies on indentation to distinguish blocks of code, the indented line breaks the program. Processing displays an error in the Console, as well as highlights the line on which the problem was encountered.

You will come to understand more about when and where to use indentation as you progress through these lessons. You may even be familiar with some programming language that uses braces ({ and }) to define blocks, in which case: Python replaces the need for these using indentation. For now, though, be aware that you need to pay careful attention to any space and tab characters that affect the indentation of your code.

Errors

Whitespace errors are not the only type logged in the console. You are likely to miss-out the odd paren, comma, or quote mark, especially when starting out. Remove the closing bracket/paren on your size function:

# dimension of the display window in units of pixels
size(500, 500

...

Now run the code and observe the console output:

"Maybe there's an unclosed paren or quote mark somewhere before this line?"

Note the white-on-red suggestion. Pretty smart, huh? To be honest, the console is not always so clear or correct, but it usually provides a clue as to where to start looking for bugs.

Colour

There are various ways to specify colour in Processing. To keep things simple, we will stick with hexadecimal values for the first tutorial. If you are familiar with graphics software like Adobe Photoshop, Illustrator, Inkscape, or Gimp, you will have seen these values in your colour mixer. Processing includes its own colour selector. To access this, select Tools > Color Selector…

The Processing Colour Selector. The hexadecimal value is the one prefixed with a #.

Screens rely on three primaries to mix colours, namely red, green, and blue. A hexadecimal colour value is comprised of 6 hexadecimal digits (0,1,2E,F) and can be split into three pairs. Each pair corresponds to a primary colour. In the case of red:
#FF0000
– the FF represents red; the middle 00 green; and right-most 00 blue. For reasons I won’t get into here, FF is the equivalent of 100%. Also, remember that you are mixing light, so #FFFFFF is white, and #000000 is black.

The fill() function sets the colour used to fill shapes. It accommodates up to four arguments, depending on the colour system you are using. For hexadecimal use the 6-digit value prefixed with a # and wrap it in quotes:

...

fill('#FF0000')

To see the fill colour in effect, add a rectangle. The rect() function is used to draw rectangles (or squares) and takes four arguments:

rect(x_coordinate, y_coordinate, width, height)

The x-coordinate values begin at the left-edge of the display window; and the y-coordinate from the top edge. Add the following code to your “hello_world” sketch; then run it to confirm that the output matches the image below:

...

fill('#FF0000')
rect(100, 150, 200, 300)
The green lines and numbers represent the rect()'s four measurements/arguments. Note that top-left corner of the rectangle is placed at the x/y coordinate.

Processing’s rect() is one of its many drawing functions. You will be introduced to some others soon.

Fills and Strokes

When you write a fill() function, every shape after that is filled in the specified colour – that is, up until the next fill() line. If you want to disable the fill altogether, use noFill(). In this way, Processing is like painting: you grab a brush and dip it in some paint, then everything you paint is influenced by the brush and colour you last selected. When you wish to paint in a different style or colour, you simply change-out your brush or dip it in a different pot.

Add the following code to your “hello_world” file. Note that comments have been added to give you a better idea of what each chunk is doing:

...

# red rectangles
fill('#FF0000')
rect(100,150, 200,300)
rect(10,15, 20,30)

# orange square
fill('#FF9900')
rect(50,100, 150,150)

# fill-less square
noFill()
rect(250,100, 150,150)

“Stroke” is another term for “outline”. There are three stroke functions you are likely to make frequent use of: stroke() to change the colour; strokeWeight() to change the width; and noStroke() to disable the stroke altogether. Like fill() and noFill(), the stroke functions affect everything below them. For a white stroke, three-pixels in width, add the following code (above the existing shapes):

...

stroke('#FFFFFF')
strokeWeight(3)

# red rectangles
fill('#FF0000')
...
Placing the stroke functions above all of your rect() lines affects everything you have drawn.

To specify if stroke corners and tips should rounded or sharp, consult the relevant reference entries: strokeCap() and strokeJoin()

Background

To change the background colour use the background() function. Add a background line to the end of your sketch:

...
rect(250,100, 150,150)

background('#004477')

Run the sketch and notice how everything has disappeared and the whole display window is a flat shade of blue. This because the background('#004477') draws over everything before it. This is useful for when you get to animation, but for now, ensure that you place your background function somewhere near the top of your code:

...
size(500, 500)
background('#004477')

...

Colour Mode

You have been using hexadecimal values to represent colour thus far. We will stick to them for the rest of the lesson, but before moving along, here is a quick introduction to some other approaches. There is no need to write any code for this section – just read through it to gain a basic grasp of the concepts involved.

For various reasons, you may need to express colours in something other than hexadecimal. Here is one such scenario: you wish to write some code that darkens the bright red fill. Firstly, consider that this shade of red:
fill('#FF0000')
can also be represented as:
fill(255, 0, 0)
As you can probably deduce, 255 is equivalent to FF (which itself is equivalent to 100%, as previously explained). To make the red half as bright, you can subtract 127 from 255. However, trying to subtract 127 from FF is more tricky because you are dealing with a mix of hexadecimal and decimal numbers.

In the previous scenario, the Processing colorMode() was set to RGB. You didn’t need to specify this, though, as it is the default mode. In this mode, values represent the screen’s primary colours. However, there is another mode you can make use of: HSB. Once set to HSB, one enters values representing Hue, Saturation, and Brightness.

The GIMP colour mixer with the HSB values outlined in green. The "H" field corresponds to Hue ring, which encircles the Saturation("S") and Brightness/Value("V") triangle.

To mimic the GIMP mixer above, one must first set the colour mode accordingly using:
colorMode(HSB, 360, 100, 100)
To explain: HSB represents the mode; 360 represents the range of degrees; and the two 100 arguments a range of 0–100% for saturation and for brightness. You would now write a red fill as:
fill(360, 100, 100)
This is because red lies at 360 degrees of rotation on the Hue ring (which begins at ‘East’ in the GIMP mixer) with the saturation and brightness set to 100%:

For a dark-greyish violet, one could use:
fill(270, 50, 50)

In HSB mode, shifting along the colour spectrum – from red to orange to yellow to green, etc. – is a matter of adding or subtracting from the H-value. Attempting the same in RGB mode is more complicated as you need to manage the quantities of each primary.

There will be more on colour in this lessons to come. Until then, you can consult the relevant reference entries should you need more detail:
colorMode()
fill()

Drawing

In this section, you will look at a number of drawing functions.

Begin a new sketch (File > New) and then save it as “drawing” (File > Save As…). Add some code to set things setup before proceeding:

size(500, 500)
background('#004477')
noFill()
stroke('#FFFFFF')
strokeWeight(3)

When you run the sketch, an empty blue display window appears. What follows below are descriptions for several drawing functions, along with some code to add to your working sketch. Feel free to experiment with the arguments to see how things respond. Each example builds on the code before it, so you’ll need to work through all of them, entering each line as you progress.

point()

Draws a point, the width of which is determined by the strokeWeight(). The arguments represent the x- and y-coordinates respectively.
Reference link: point()

point(100, 25)
point(200, 25)
point(150, 75)

triangle()

Draws a triangle. The six arguments represent three x/y-coordinate pairs.
Reference link: triangle()

triangle(100,25, 200,25, 150,75)

ellipse()

Draws an ellipse. The first pair of arguments represent an x/y coordinate that marks the centre of the ellipse; the second pair of arguments represent its width and height.
Reference link: ellipse()

ellipse(100,100, 100,50)

Ellipse and Rect Modes

You have seen how rectangles are drawn from the top-left corner, and ellipses are centred on the x/y coordinate. If you wish to change this behaviour – for example, have the rect function draw from the center – refer to the following functions:
ellipseMode()
rectMode()

quad()

Draws a quadrilateral (a four-sided polygon). The eight arguments represent four x/y-coordinate pairs.
Reference link: quad()

quad(250,250, 350,300, 380,400, 260,380)

line()

Draws a straight line between two points. The first pair of arguments represent the starting x/y coordinates; and the second pair, the ending x/y coordinates.
Reference link: line()

line(390,380, 460,320)

Rainbow Task

Time for a challenge!

Begin a new sketch (File > New) and then save it as “rainbow” (File > Save As…).

Add some code to get started:

size(600, 600)
background('#004477')
noStroke()

Using what you’ve learnt thus far, complete the rainbow below:

Clue: you can overlap shapes to mask-off others.

Variables

Variables are placeholders for information – much like when you use letters in algebra to represent a value. In fact, Python variables look and behave similarly.

Begin a new sketch and then save it as “variables”. To keep things simple, we’ll print to the Console area. Add the following code:

size(600, 400)
background('#004477')

print(width)
print(height)

width and height are system variables that store the dimensions of the display window. However, you are not limited to system variables. When declaring your own variables, you assign a value using an = sign (assignment operator). Try this out with a new variable named “x”:

...

x = 1
print(x) # displays 1 in the console

You may name your variables whatever you wish, provided the name: contains only alpha-numeric and underscore characters; does not begin with a number; and does not clash with a reserved Python keyword or variable (like width). For example:

playerlives = 3  # correct
playerLives = 3  # correct
player_lives = 3 # correct
player lives = 3 # incorrect (contains a space)
player-lives = 3 # incorrect (contains a hyphen)

Whether you should name a variable using camelCase, underscores, or some other convention is a matter of style (and vociferous debate). However, it is good to decide upon and stick to a naming convention, as you will make extensive use of variables in Processing. Add three more variables to your script, using them as arguments in a rect() function:

...

x = 1
print(x) # displays 1 in the console
y = 30
w = 20
h = w
rect(x,y, w,h)
Note how the w variable has been assigned to h, resulting in a square.

Arithmetic Operators

Variables are far more useful when you perform arithmetic operations using them. Add the following code to your “variables” sketch:

...
rect(x,y, w,h)

print(x + 2)

I’m guessing that did exactly what you expected? You can also subtract:

...
rect(x,y, w,h)

print(x + 2)       # displays 3
print(x - 2)       # displays -1

Multiplication is performed using the * operator:

...
print(x * 5)       # displays 5

Now add the code below, but before hitting run, see if you can predict what the total is:

...
print(1 + 2 * 3)   # displays ???

The console displays a 7 – and not a 9 – because multiplication occurs before addition. Certain operators take precedence over others. Remember BEDMAS? Or BODMAS (depending on where you’re from)? It is an acronym to help you recall the order of operations. If you want to override this order, use brackets:

...
print(1 + 2 * 3)   # displays 7
print((1 + 2) * 3) # displays 9

For division, use a forward-slash:

...
print(4 / 2)       # displays 2

Be aware, though, that dividing two integers always produces an integer result (integers are ‘whole’ numbers, as opposed to those with a decimal point). For example:

...
print(3 / 2)       # displays 1

Python discards any decimal digits, effectively ‘rounding-down’ the result. To allow decimal results, define at least one of your operands using a decimal point:

...
print(3 / 2.0)     # displays 1.5

Decimal numbers are usually referred to as floating point, or float values, in programming terminology.

Of course, division by zero operations will result in errors:

Modulo Operator

The modulo operator is written as a percentage sign (%). It calculates the remainder of a division operation. Take five divided by two as an example:

  • one could say the answer is 2.5;
  • or, that the answer is 2 remainder 1, because two ‘goes into’ five twice with one left over.

The modulo operator performs the latter. Here is some code contrasting a division and modulo operation:

print(5.0 / 2)     # displays 2.5
print(5.0 % 2)     # displays 1

It may not be evident why this is operator useful. However, many important algorithms – such as those used in cryptography – make use of it. For now, consider that modulo operations resulting in a 0 indicate that numbers divide exactly. Among other uses, this is handy for establishing whether a number is odd or even:

print(7 % 2)       # displays 1, therefore 7 is odd
print(6 % 2)       # displays 0, therefore 6 is even

You will be making use of modulo operators in future lessons.

Image Reveal Task

Time for another challenge!

The idea here is to follow the instructions to reveal a symbol. Create a new sketch and save it as “symbol_reveal”. Add some code to get started:

size(600, 740)
background('#004477')
noFill()
stroke('#FFFFFF')
strokeWeight(3)

xco = 400
yco = 440

To begin revealing the symbol, follow instruction steps 1 to 6. To get you started, here is the first instruction, along with the correct code:

  1. Draw a line beginning at an x-coordinate of half the display window width, and y-coordinate of a third of the window height. The endpoint must have an x/y-coordinate equal to xco & yco.

– which will be coded as follows:

line(width/2,height/3, xco,yco)
Instruction 1 of 6.

Now, carry-out the rest of the instructions:

  1. Draw a centred ellipse with a width that is an eleventh of the display window width, and a height that is a fourteenth of the window height.
  2. Draw a centred ellipse with a width that is a nineteenth of the display window width, and a height that is a twenty-second of the window height.
  3. Draw a line beginning at an x/y-coordinate equal to xco & yco respectively. The endpoint must have an x-coordinate of the display window width minus xco, and a y-coordinate equal to yco.
  4. Draw a line beginning at an x-coordinate of the display window width minus xco, and y-coordinate equal to yco. The endpoint must have an x-coordinate of half the display window width, and a y-coordinate of a third of the window height.
  5. Draw a centred ellipse with a width that is a fifth of the display window width, and height that is a twelfth of the display window height.

Clue: if this seems like a conspiracy, you may be on the right track.

Disk Space Analyser Task

Here is the final challenge before moving onto lesson 02. However, before tackling this one, you will need an introduction to drawing arcs.

The arc() function is used to draw elliptical arcs. It’s best to try out a few examples to see how it works. Create a new sketch and save it as “disk_space_analyser”. Add some code to get things started:

size(600,700)
background('#004477')
stroke('#FFFFFF')
strokeWeight(3)
noFill()

The arc() function takes the following arguments, expanded across multiple lines here for easier comprehension:

arc(
  x_coordinate, y_coordinate,
  width, height,
  start_angle, end_angle
)

Add an arc to your sketch using a start_angle of 0 and end_angle of 2

...
noFill()
arc(width/2,height/2, 200,200, 0,2)
arc(width/2,height/2, 200,200, 0,2)

The green overlay helps illustrate how things work. The arc is drawn along the perimeter of an invisible ellipse that is centred on the given x/y coordinates. An angle of 0 is positioned at ‘East’, which then opens clockwise to an angle of 2 – which looks more like, around, 115 degrees of rotation? The reason for this is that Processing uses radians and not degrees. Why radians? Well, to answer a question with a question: why are there 360 degrees in a whole circle, anyhow? I mean, why not 500 or 100, or 1 million degrees? In fact, while we’re on the topic, why are there 60 minutes in an hour? Or 24 hours in a day? The answer has to do with influences from ancient civilisations and is beyond what you need to know for now. What you do need to know is that radians are a standard unit of angular measure used in many areas of mathematics. Rather than try to explain in writing how large/small a radian is, here is a neat animation from Wikipedia:

The SI symbol for radian is rad.
Lucas V. Barbosa [Public domain], from Wikimedia Commons

There are plenty of online degree-to-radian converters out there, but it’s good to know a few key measurements. For starters, zero degrees is equal to zero radians, and 180 degrees is equal to approximately 3.142 radians. You recognise that number? Yep, it’s pi! Therefore, 360 degrees is equal to 2π. Add the following code:

...
noFill()
arc(width/2,height/2, 200,200, 0,2)
arc(width/2,height/2, 300,300, 0,PI)   # half-circle
arc(width/2,height/2, 400,400, 0,PI*2) # full-circle
PI is a Processing variable representing π to 20 decimals places.

To close an arc, so as to form a ‘slice’, add an additional PIE parameter:

...
noFill()
arc(width/2,height/2, 200,200, 0,2)
arc(width/2,height/2, 300,300, 0,PI)   # half-circle
arc(width/2,height/2, 400,400, 0,PI*2) # full-circle
arc(width/2,height/2, 350,350, 3.4,(PI*2)-(PI/2), PIE)

A disk usage analyser presents a graphical representation of what is on a disk drive. The Linux GNOME Disk Usage Analyzer is one example of such software. You will recreate the storage ring-chart below. The text and number labels have been added to assist you; you need not add them to your recreation.

Lesson 02

That’s it for lesson 01. I hope it was enjoyable, if somewhat challenging. Take a break – you deserve it! When you are ready to move on, proceed to lesson 02 using the link below. If you are familiar with vector graphics (Adobe Illustrator, Inkscape, etc.), you’ll be gaining some new insight into their inner-workings.

Begin Lesson 02: Bézier, Catmull, and Rom walk into a bar …

Complete list of Processing.py lessons

References

  • http://py.processing.org/reference/