Tuesday 14 April 2009

Vertex Functions

Vertex Functions

A vertex is basically another word for a point. Yet the syntax of a vertex() function is notably different to that of the point() function.

For example in order to draw two points on a display screen using the point() function you could call a similar code to that shown in code 1..

Code 1;

size(200,200);
background(0);
strokeWeight(10);
stroke(255);
smooth();
point((width*0.2),height/2);
point((width*0.8),height/2);


Figure 1: vertex() and point() examples Sketch.

Code 2 ;

size(200,200);

background(0);
strokeWeight(10);
stroke(255);
smooth();
beginShape(POINTS);
vertex((width*0.2), height/2);
vertex((width*0.8), height/2);
endShape();

Code 2 shows how the use of a vertex() function would produce the same outcome (See figure 1) as using the point() function. The display area is broken down into coordinate locations (200 in width by 200 in height for this example) each of these references a point location or vertex, which are the arguments taken by each of the two functions.

You will probably have noticed that there is an inclusion of beginShape() function, and endShape() function, which the vertex() function can be found nestling in. The beginShape() function has predefined mode arguments, these are POINTS, LINES, TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN, QUADS, and QUAD_STRIP, if no mode is defined line strip is used instead. The beginShape() function essentially begins the recording of the drawing instructions and endShape() stops the recording. The benefit of this is that the data can be plotted based on different drawing algorithms, using the different modes within the beginShape() and endShape() functions

The vertex() functions we saw above are specifically for displaying 2D coordinates where two arguments are fed to the function. The vertex() function also allows for displaying 3D coordinates with the use of three arguments and a display renderer such as P3D or opengl. The syntax of both are as follows;

vertex(x, y);

vertex(x, y, z);

Just to take a moment out in order to see some vertex() functions in action, we have seen a simple use of the POINTS mode (code 2) so this time lets have a look at the mode TRIANGLES (code 3 and figure 2)

Code 3;

size(200,200);
background(0);
stroke(255);
strokeWeight(4);
smooth();
noFill();
beginShape(TRIANGLES);
strokeJoin(ROUND);
vertex((width*0.2), height*0.8);
vertex((width*0.8), height*0.8);
vertex(width/2, (height*0.2));
endShape();

Figure 2: TRIANGLE mode sketch example.

You should be familiar by now with the size() and background() functions. The stroke() function takes an argument from 0 to 255, with 0 being black and white being 255. The strokeWeight() function is the thickness of the line. The noFill() function ensures that the area enclosed within the shape isn't automatically filled. For example if the noFill() function were to be omitted then the display would be that of a solid white triangle.

There were two functions that I purposely left out as they require slightly more detail, the smooth() function and the strokeJoin() function.

The strokeJoin() function can take three different options. The one we have seen, ROUND, but also MITER which is the default option and BEVEL. These effect the way the end caps come together, and as you can see from figure 2, the points of the triangle are slightly rounded. We will come back to this later on in the chapter.

In order for us to see how the internal recording works the following sketch has a look at recreating the functionality found with the vertex() function, but with the use of the point() and line() function. (Code 4) To produce figure 3.


Code 4;

void setup(){

size(200, 200);

background(0);

//Sets up arrays to store coordinates

float[]x = new float[5];

float[]y = new float[5];

//sets up 2D array to group together coordinates

float [][]xy = {x, y};

//Creates a starting point from which to run

float pointsX = width*0.1;

float pointsY = height*0.9;

//sets the starting points

xy[0][0] = pointsX;

xy[1][0] = pointsY;

//iterates over each of the positions in the array's to assign

//coordinates dependent on the previous coordinates

for (int i = 1; i<xy[o].length; i++){

if(i ==1){

pointsX +=width*0.4;

pointsY -=height*0.8;

xy[0][i] = pointsX;

xy[1][i] = pointsY;

}

if(i==2){

pointsX +=width*0.4;

pointsY +=height*0.8;

xy[0][i] = pointsX;

xy[1][i] = pointsY;

}

if(i==3){

pointsX -= width*0.8;

pointsY -=height*0.55;

xy[0][i] = pointsX;

xy[1][i] = pointsY;

}

if(i==4){

pointsX +=width*0.8;

xy[0][i] = pointsX;

xy[1][i] = pointsY;

}

}

star(xy);

}

//function called to plot a star and join the points.

void star(float[][]pts){

stroke(255);

smooth();

for(int i = 0; i<pts[0].length; i++){

strokeWeight(5);

//Plot the points from the input arrays

point(pts[0][i], pts[1][i]);

strokeWeight(0.5);

//Join the points together with a thin line

if(i>0){

line(pts[0][i],pts[1][i],pts[0][i-1], pts[1][i - 1]);

}

//If the last position in the array join to the first point.

if(i == pts[0].length-1){

line(pts[0][i], pts[1][i], pts[0][0], pts[1][0]);

}

}

}

Figure 3: Star Plotter Sketch

At first glance the code may look quite daunting, however as we go through it you will find the majority familiar with what has previously been covered.

First off, the function setup() which is used to setup the display size, the colour is set and several array's are created and then populated. As we can recall the use of magic numbers is considered poor coding technique, so each position in the array has been setup as a percentage of the width and height assigned to the x and y coordinates. In this example consider if we had used magic numbers and the size of the display is changed. The output sketch in the display will be the same size regardless of the box size, however with the use of percentages dependent on the initial setup size, the sketch will always be proportional in size to the size of the display.

You may be wondering what i<xy[0].length is referring to within the for() statement . Recall that a for() statement has several properties, the first is the initialization, int i = 0; , the second, which is what we are referring to in this instance, the condition and the third is the counter i++;. So i<xy[0].length is a condition, This condition is stating that the statement should continue running unless the value of i is greater than or equal to the amount of positions or length in the array containing the coordinates. For the above example (code 4) xy[0].length would be equal to 5.

Next we move on to the custom star() function, This is where all the points are plotted and then joined together. As seen in the setup() function, the for() statement is used to iterate over each of the defined xy coordinates. The points are then plotted with the use of the point() function. The next two if() statements act depending on the value of the integer i. The first draws a line between the current values for the x y coordinates and joins it to the previous array containing the x and y coordinates with the use of pts[0][i-1] and pts[1][i-1] as long as the point isn't that of the starting point. The second if() statement runs only if the position in the array is equal to the final position of the array, and joins that coordinate to that of the starting coordinate and thus completing the star.

We can see quite clearly from the above example (code 4) how the internal recording works. Which if we had replaced the star() function to that below (code 5), it isn't overly clear how the process works, but will enable easier manipulation by using different algorithms to produce different shapes.

Code 5 ;

void star(float[][]pts){

stroke(255);
strokeWeight(5);
smooth();

//draws the points of the star

beginShape(POINT);
noFill();

for (int i = 0; i<pts[0].length; i++){

vertex(pts[0][i], pts[1][i]);

}

endShape();

//Change the thickness of the joining lines

strokeWeight(0.5);

beginShape();

noFill();

for (int i = 0; i

vertex(pts[0][i], pts[1][i]);

}

endShape(CLOSE);

}

The following code (code 6) shows how a small change in the function can cause a completely different pattern of shapes, opening up many possibilities to be explored.

Code 6;

void triangleExplosion(float[][]pts){stroke(255);

strokeWeight(5);

smooth();

beginShape(TRIANGLES);

//Draw Triangles

for(int i = 0; i<120;i++){

stroke(random(0,200));

//Randomize the colouring of the shapes

fill(random(225,255),150);

strokeWeight(random(0.5,2));

//Create random numbers

Random num = new Random();

//call a random integer between 0 and 5 and assign it to n

int n = num.nextInt(pts[0].length);

vertex(random(pts[0][n]),random(pts[1][n]) );

}

endShape();

}

Figure 4: triangleExplosion Sketch

As can be seen from figure 4, the output couldn't be any different. There are a few things to note from this coding. First of all I changed the function name to triangleExplosion(), this could have been left as star() however it is a good idea to have meaningful names for functions in order to give some idea of what the function does.

The fill function has two arguments, the first argument, random(225,255), sets a colour with the value being randomly selected from the values between 225 and 255. The second argument set as 150 deals with the transparency of the colouring, enabling the triangles drawn underneath to still be visible.

Finally random integers are created to randomly pick which array coordinates are chosen to form the triangles.


Anti-aliasing

As mentioned previously the function smooth()needs further explanation. The use of this function anti-aliases the output. In other words this is a function to minimize the distortion viewed by an image. The smooth function basically, as it's name suggests smooths the edges of the images displayed by the running of the coding. In order to see this lets have a look at this simple example;


code 7 ;

size(300,300);

strokeWeight(2);

background(0);stroke(255);

fill(random(225,255),150);

float x = 0.3;

for(int i = 0; i<4;>

if (x > 0.5){

smooth();

ellipse(width*x,height*x,width/2,height/2);

}

else{

ellipse(width*x,height*x,width/2,height/2);

}

x = x + 0.12;

}




Figure 5: Circles and Anti-aliasing

The example above (code 7) should be very familiar too you by now. First of all the size of the output is set, along with the thickness of the drawn lines, by the use of the strokeWeight() function. The background is set to black and the stroke colour is set to white stroke(255) so its easy to show the differences. The fill function is used to colour in the drawn shapes and I have opted to add some transparency on to further help define the edges. I wanted to draw four circles so a for statement is used to create the four circles at differing positions, where the use of the if statement controls which shapes will appear with anti-aliasing on or off.

As we can see from the example (figure 5) the two circles drawn in the top right have the ant-aliasing function off, which is the default setting in processing. In order to switch it on the function smooth() must be added. This is the case for the two circle's toward the bottom right. If you notice the difference in outline producing a smooth flowing circle when anti-aliasing is switched on. However this function can have an impact on the performance of the coding as the anti-aliasing calculations take time.

No comments: