An Introduction To Programming With Processing

Drawing Irregular Shapes

Fortunately we are not limited to drawing only 2D primitives in Processing, but we can in fact draw any 2D shape that we can imagine. In order to draw irregular or non-primitive 2D shapes we use two main functions, beginShape() and endShape() issued in that particular order, in conjunction with several calls to another function called vertex(). We're going to start off by experimenting with this feature by drawing a speech bubble surrounding the “Hello World” text in our sketch. The beginShape() function tells Processing to start recording the following set of points that are about to be issued until the endShape() function tells Processing to stop recording. Between these two functions we are going to supply Processing with a set of special X and Y coordinates that will be parameters in a call to the vertex() function. The vertex() function accepts parameters in various formats of which we are most interested in the X and Y parameter format, which will be used to determine where (in terms of X and Y) the point (or vertex) to which the function is referring to will be placed. By adding the following statements in the particular order listed below we will be able to draw a speech bubble:
//Start the shape in the middle of the Display Window's x axis

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);

//The following statement marks the point where the shape 
//starts to mirror itself

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);

//this is the arrowhead that points out of the
//speech bubble towards the smiley face

vertex(width/2 + 50, (height/4)*3-35);
vertex(width/2 + 40, (height/4)*3-55);
vertex(width/2 + 30, (height/4)*3-35);

//the end of the shape is the same as 
//the beginning

vertex(width/2,    (height/4)*3-35);

Irregular Shapes
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 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);
Patterns In Code Identified with Implicit Programming
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.
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:
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:
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:
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.
Stroke Overlap
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.
Stroke Caps Options
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: