Tutorial Processing: bezier, text, textFont, class with variables and

advertisement
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;
}
}
Download