Tutorial Processing: bezier, text, textFont, PFont, class with variables and methods, switch case, arrays: float[] and String[] This sketch demonstrates drawing connected Bézier curves. After the first set of 4 points are specified, the second anchor becomes the first anchor point and 3 more points specify the next portion of the curve. My sketch provides the facility to re-start the curve and to clear the screen to start all over. The sketch also provides a way to change the color of the curve. In terms of implementation, this sketch demonstrates displaying text, using classes to implement the buttons, the case statement and arrays. The web site has 3 sketches. These notes focus on the 3rd sketch, with the buttons implemented using a programmer defined class (that is, the MButton class I wrote). You will benefit from looking at the code of the other two sketches. Bezier curves Bezier curves are a way of specifying curves invented by Pierre Bézier. The challenge is how to define a smooth curve that is not part of a circle, ellipse, and so on. Bézier's method makes use of what are called parametric curves. A parameter, often denoted by t which can be thought of as t for time is used with vectors to define the curve. The t value goes from 0 to 1. A circle is expressed as a parametric curve by defining the X and Y parts separately: X(t) = cos(t) Y(t) = sin(t) A cubic Bézier curve is defined in terms of two anchor points (A1 and A2) and two control points (C1 and C2). [The points also can be viewed as vectors.] The curve starts at A1 and ends at A2. The curve moves from A1 towards C1 and then towards C2 before heading towards and ending up at A2. The parametric curve is defined as a weighted sum of the 4 points: P(t) = (1-t)3 * A1 + 3*t*(1-t)2*C1+3*t2*(1-t)*C2+t3*A2 Processing provides a function that draws the Bézier curve defined by the points specified by the 8 parameters representing 4 x, y positions: bezier(a1[0],a1[1],c1[0],c1[1],c2[0],c2[1],a2[0],a2[1]); The color of the curve is the current stroke color. In this sketch, I use variables strcol, stgcol and stbcol to hold the red, green and blue components indicating the color to be used and noFill(); If there is a fill value, the bezier function draws a shape with the curve completed with a line from anchor 1 to anchor 2. Actually, the objective of this sketch is to practice drawing Bézier curves. Work with the application to gain understanding of how sets of anchor points and control points produce curves. Text Displaying text using Processing requires several distinct statements. The code must declare and define at least one variable for the text font, set the font for text and then use the text statement. A variable of datatype PFont must be declared, for example: PFont myfont; Next, you must write code to assign a value to each PFont variable. myfont = createFont("Ariel",18); This code assumes that the font Ariel is available on the computer running the sketch, which typically is true. The size is 18. The textFont statement sets the text font for subsequent use: textFont(myfont); Lastly, the text statement writes text, using the current font, at the specified location: text (btext,xpos+2, ypos+18); This is a statement that appears in the drawit method to be described later. It causes the string contained in the variable btext to be drawn at x = xpos+1, and y = ypos+18. To determine which fonts are available on the computer, use String[] fontList = PFont.list(); println(fontList); This displays all the available fonts in the print area below the edit area in the Processing Environment. This code would not be part of the Bézier sketch! The loadFont function provides a way to make the font part of the sketch itself, which would make an exported sketch larger as an applet or application, but also more robust. You need to look up how to use this in Help/Reference. Class variables and methods In this application, I use rectangles displayed on the screen, some with text and some without, to be buttons. The player/user clicks the mouse on the button. In my first attempt at this, I created the buttons individually. [Check out the second of the three sketchs.] A better approach is to define a class. A class consists of variables and functions, also called attributes or properties and methods. To write code to be used by all the buttons, I need to think about what defines a button: what attributes a button needs to have, and what actions—what code—is required. What I came up with is that buttons are defined by a position on the screen, width and length, color, and text. The declarations are: MButton cr = new MButton(740,100,50,50,255,0,0,""); MButton cg = new MButton(740,200,50,50,0,255,0,""); MButton cb = new MButton(740,300,50,50,0,0,255,""); MButton resetb = new MButton(650,50,75,30,200,0,100,"Re-start"); MButton clearb = new MButton(740,50,50,30,200,0,100,"Clear"); NOTE: these declarations are at the top of the sketch code. I wrote them after I wrote the code the defines the MButton class and the Processing system refers to the class definition when executing the declaration. The first method in the class definition is termed the constructor. It is the one named MButton. In this situation, the constructor simply sets the internal variables (the properties) to the values passed in as parameters. The actions for a button are: drawing the button and checking if the mouse is on top of the button. I made the checking method, named onbutton, be more general, since I made it take an x and y as parameters instead of using mouseX and mousey directly. Here is my code for the class MButton. class MButton { int xpos, ypos, xlen, ylen; int rcol, gcol, bcol; String btext; MButton (int x, int y, int w, int h, int r, int g, int b, String t) { xpos = x; ypos = y; xlen = w; ylen = h; rcol = r; gcol = g; bcol = b; btext = t; } void drawit() { fill(rcol,gcol,bcol); rect(xpos,ypos,xlen,ylen); fill(0,0,0); textFont(myfont); text(btext,xpos+2,ypos+18); } boolean onbutton(int x, int y) { if ((x>=xpos)&&(x<=(xpos+xlen))&&(y>=ypos)&&(y<=(ypos+ylen))) return true; else return false; } } It may be possible to put the action to be done upon clicking on the button as one of the properties/attributes of the button, but I have not figured out how to do that yet. Instead, a function named checkbuttons goes through the buttons and does the appropriate action. Similarly, there may be a more elegant and efficient way to distinguish the buttons used for color and the other buttons using a feature called inheritance. There are Java classes that can be imported to set up user interfaces, including buttons. If you are building a complex application, it may pay to investigate the use of code already written. Of course, the same can be said for all applications. Arrays Arrays are sets of values. Array variables are declared and initialized in various ways. Individual elements are referenced and set using square brackets. One tricky thing to remember is that the indexing starts at 0, not 1. The referencing value is called the index. For example, in this sketch, I make use of an array of messages to direct the player/user to set anchor 1, control 1, control 2 and anchor 2. The global variable is String[] msgs = new String[4]; I code statements in the setup function to set the individual values: msgs[0] ="Set anchor 1"; msgs[3] = "Set anchor 2"; msgs[1] = "Set control 1"; msgs[2] = "Set control 2"; Similarly, the anchor points and control points are themselves set up as arrays float[] a1 = new float[2]; float[] a2 = new float[2]; float[] c1 = new float[2]; float[] c2 = new float[2]; After defining cr, cg, and cb each as datatype MButton, the class I defined, I set up an array holding these buttons with the statement: MButton[] colorbuttons = {cr,cg,cb}; You may look at the tetrahedron tutorial for a slightly different approach: defining a Pt class to hold points. The switch case statement The switch case construct is a type of conditional statement. A variable is compared to each of several values and the code for the value that matches is executed. The switch statement is done so code in cases can be combined (see the days of the month example), so the break; command must be used if you do not want this to happen. The switch case statement is demonstrated well with the days of the month. Suppose the variable mname held a 3 letter abbreviation indicating the month, then the following would print out in the print area the number of days. switch(mname) { case "Sep": case "Apr": case "Jun": case "Nov": println("30 days"); break; case "Feb": println("28 or 29 days"); break; default: println("31 days"); } In this sketch, I use a switch case statement in the mousePressed function with a variable turn that indicates which of the anchor 1, control 1, control 2 and anchor 2 positions are being set. The code is: switch(turn) { case 0: a1[0] = mouseX; a1[1] = mouseY; break; case 3: a2[0] = mouseX; a2[1] = mouseY; break; case 1: c1[0] = mouseX; c1[1] = mouseY; break; case 2: c2[0] = mouseX; c2[1] = mouseY; break; } There is no default option. The variable turn is adjusted later in the function. Implementation The code for the sketch starts with the following global variables, each of which has been explained. MButton cr = new MButton(740,100,50,50,255,0,0,""); MButton cg = new MButton(740,200,50,50,0,255,0,""); MButton cb = new MButton(740,300,50,50,0,0,255,""); MButton[] colorbuttons = {cr,cg,cb}; MButton resetb = new MButton(650,50,75,30,200,0,100,"Re-start"); MButton clearb = new MButton(740,50,50,30,200,0,100,"Clear"); PFont myfont; float[] a1 = new float[2]; float[] a2 = new float[2]; float[] c1 = new float[2]; float[] c2 = new float[2]; String[] msgs = new String[4]; int turn =0; int strcol = 0; int stgcol = 255; int stbcol = 0; The MButton declarations do not actually cause anything to be displayed on the screen. What they do is 'stash' away the information that defines a button. The display will be accomplished by calls to the drawit methods from the drawbuttons function. The functions for the sketch consist of the setup, draw, and mousePressed definitions, the all-programmer supplied drawbuttons and checkbuttons, and the class definition MButton. The calling table is Function or method Called by setup draw mousePressed drawbuttons checkbuttons onbutton method drawit method Processing Processing Processing draw mousePressed checkbuttons drawbuttons The player/user action involves clicking the mouse. The program must determine if this is clicking on a button or defining one of the 4 points. This is handled by checking on the buttons first and then returning to the rest of the mousePressed function. The function and class definition code is: void setup() { size(800,600); background(255); myfont = createFont("Ariel",18); msgs[0] ="Set anchor 1"; msgs[3] = "Set anchor 2"; msgs[1] = "Set control 1"; msgs[2] = "Set control 2"; frameRate(6); } void draw() { textFont(myfont); fill(0); text(msgs[turn],10,50); drawbuttons(); } void drawbuttons() { fill(0); textFont(myfont); text("Click mouse to set anchor and control points for continuous Bezier curve.",50,20); for (int i=0;i<colorbuttons.length;i++) { colorbuttons[i].drawit(); } resetb.drawit(); clearb.drawit(); } void mousePressed() { if (checkbuttons()) { stroke(strcol,stgcol,stbcol); triangle(mouseX,mouseY, mouseX, mouseY+3,mouseX+3,mouseY); switch(turn) { case 0: a1[0] = mouseX; a1[1] = mouseY; break; case 3: a2[0] = mouseX; a2[1] = mouseY; break; case 1: c1[0] = mouseX; c1[1] = mouseY; break; case 2: c2[0] = mouseX; c2[1] = mouseY; break; } turn++; if (turn<4) { //erasing old message fill(255); noStroke(); rect(10,30,300,60); fill(0); text(msgs[turn],10,50); } else { turn=1; //erasing old message fill(255); noStroke(); rect(10,30,300,60); noFill(); // fill(255,0,0); stroke(strcol,stgcol,stbcol); bezier(a1[0],a1[1],c1[0],c1[1],c2[0],c2[1],a2[0],a2[1]); a1[0]= a2[0]; a1[1] = a2[1]; fill(0); text(msgs[turn],10,50); } } } boolean checkbuttons() { //returns false if button clicked, true if not //does action if (clearb.onbutton(mouseX,mouseY)) { background(255); drawbuttons(); turn=0; text(msgs[0],10,50); return false; } else if (resetb.onbutton(mouseX,mouseY)) { turn=0; fill(255); noStroke(); rect(10,30,300,60); fill(0); text(msgs[0],10,50); return false; } for (int i=0;i<colorbuttons.length;i++) { if (colorbuttons[i].onbutton(mouseX,mouseY)) { strcol = colorbuttons[i].rcol; stgcol = colorbuttons[i].gcol; stbcol = colorbuttons[i].bcol; return false; } } return true; } class MButton { int xpos, ypos, xlen, ylen; int rcol, gcol, bcol; String btext; MButton (int x, int y, int w, int h, int r, int g, int b, String t) { xpos = x; ypos = y; xlen = w; ylen = h; rcol = r; gcol = g; bcol = b; btext = t; } void drawit() { fill(rcol,gcol,bcol); rect(xpos,ypos,xlen,ylen); fill(0,0,0); textFont(myfont); text(btext,xpos+2,ypos+18); } boolean onbutton(int x, int y) { if ((x>=xpos)&&(x<=(xpos+xlen))&&(y>=ypos)&&(y<=(ypos+ylen))) return true; else return false; } }