The display we are aiming to achieve is shown in figure 41.1
Figure 41.1 The final animation
The code for this is shown below:
// coordinates of the vertices
float[] xCoords;
float[] yCoords;
// the number of vertices
int numberOfVertices;
// the screen size
int screenXSize;
int screenYSize;
// speed (number of pixels) that the vertices move
int speed;
void setup() {
// initialise
screenXSize = 600;
screenYSize = 400;
speed = 2;
numberOfVertices = screenXSize;
xCoords = new float[numberOfVertices];
yCoords = new float[numberOfVertices];
// setup display variables
size(screenXSize, screenYSize);
background(0);
smooth();
strokeWeight(10);
stroke(255);
// initialise each vertex to be the middle of the display
for (int i = 0; i < numberOfVertices; i++) {
xCoords[i] = screenXSize / 2;
yCoords[i] = screenYSize / 2;
}
}
void draw() {
// clear the screen by resetting the background
background(0);
// begin drawing the shape
beginShape();
// for every vertex
for (int i = 0; i < numberOfVertices; i++) {
// draw the vertex at the coordinates
vertex(xCoords[i], yCoords[i]);
// draw the point at the coordinates
// point(xCoords[i], yCoords[i]);
// create a random direction for next time
float randomXDirection = random(-speed, speed);
float randomYDirection = random(-speed, speed);
// add the random direction to the existing coordinates
xCoords[i] = xCoords[i] + randomXDirection;
yCoords[i] = yCoords[i] + randomYDirection;
}
endShape();
}
Don't worry about understanding this code yet as we will walk through it. What we are attempting to produce is the image in figure 41.1 – a 'sparkler' type effect that will animate and grow in a random pattern. Each point in the animation will be separate from the others so that the sparkling will grow as each point moves away from the others.
We will start with using the vertex function similarly to page xx by simply creating a 'dot' on the screen, as shown in figure 41.2
Figure 41.2 A single vertex
The code for this is fairly simple:
// the screen size
int screenXSize;
int screenYSize;
void setup() {
// initialise
screenXSize = 600;
screenYSize = 400;
// setup display variables
size(screenXSize, screenYSize);
background(0);
smooth();
strokeWeight(10);
stroke(255);
}
void draw() {
// begin drawing the shape
beginShape();
// draw the vertex at the coordinates
vertex(screenXSize / 2, screenYSize / 2);
endShape();
}
Here we are simply creating a window of size 600 x 400 with a black background and drawing a single white vertex in the middle of the screen (where screenXSize / 2 is the size of the x-axis divided by 2, so the middle). As seen on page xx, the smooth() function is the anti-aliasing function that gives a smoother appearance to lines and shapes. Adding this function displayes the image with smoother lines and is more appealing to look at. Try removing it and you will see that the dot has jaggedy lines, whereas adding smooth() results in a circular looking dot
As we are using the Processing continuous mode (in the above example the dot appears static because it is being drawn over the top of the previous one each time the draw() method is run), we can introduce some animation by moving the vertex each time. We can add some variables to store the current coordinates of the vertex, and a speed variable to control how much it moves each time
float xCoords;
float yCoords;
int speed;
In the setup() method we can initalise these so that the starting coordinates are the middle of the screen.
speed = 2;
xCoords = screenXSize / 2;
yCoords = screenYSize / 2;
The coordinate variables can then be accessed and updated in the draw() method by drawing the vertex at the value of the coordinates, then updating them by adding the value of the speed variable, so that next time the draw() method is run, the coordinates will have changed and the dot will appear to move
beginShape();
// draw the vertex at the coordinates
vertex(xCoords, yCoords);
endShape();
// add the speed direction to the existing coordinates
xCoords = xCoords + speed;
yCoords = yCoords + speed;
The updated program is shown below
// coordinates of the vertices
float xCoords;
float yCoords;
// the screen size
int screenXSize;
int screenYSize;
// speed (number of pixels) that the vertices move
int speed;
void setup() {
// initialise
screenXSize = 600;
screenYSize = 400;
speed = 2;
xCoords = screenXSize / 2;
yCoords = screenYSize / 2;
// setup display variables
size(screenXSize, screenYSize);
background(0);
smooth();
strokeWeight(10);
stroke(255);
}
void draw() {
// clear the screen by resetting the background
background(0);
// begin drawing the shape
beginShape();
// draw the vertex at the coordinates
vertex(xCoords, yCoords);
endShape();
// add the speed direction to the existing coordinates
xCoords = xCoords + speed;
yCoords = yCoords + speed;
}
If you run this you will see that the dot now moves off the screen, from the middle towards the bottom-right, because we are adding the value of the speed variable to the coordinates each time. The illusion of movement is enhanced by clearing the screen each time by setting the background each time
We can now introduce some random movement to the vertex by randomising the direction it will move by using the random() function seen on page xx to randomise the speed variable each time. To do this, all we need to do is replace the following lines in the draw() method
// add the speed direction to the existing coordinates
xCoords = xCoords + speed;
yCoords = yCoords + speed;
with the lines
// create a random direction for next time
float randomXDirection = random(-speed, speed);
float randomYDirection = random(-speed, speed);
// add the random direction to the existing coordinates
xCoords = xCoords + randomXDirection;
yCoords = yCoords + randomYDirection;
This piece of code creates a random number between the values -speed and speed (currently set to 2) and uses the random value to set the coordinate values for the next time the vertex is drawn. This makes the draw method look like the below
void draw() {
// clear the screen by resetting the background
background(0);
// begin drawing the shape
beginShape();
// draw the vertex at the coordinates
vertex(xCoords, yCoords);
endShape();
// create a random direction for next time
float randomXDirection = random(-speed, speed);
float randomYDirection = random(-speed, speed);
// add the random direction to the existing coordinates
xCoords = xCoords + randomXDirection;
yCoords = yCoords + randomYDirection;
}
If you run this, you will see that the the dot now moves around its starting point in a random fashion. This is due to the random value added to the coordinates that means the dot moves slightly each time it is drawn.
The next stage of our animation is to create more vertices. This is where our program begins to become a bit more complex. In order to create many vertices we need to issue the vertex() function as many times as we want them created. For a few vertices it would be acceptable to just write the command that many times such as
vertex(xCoords + random(-speed, speed), yCoords + random(-speed, speed));
vertex(xCoords + random(-speed, speed), yCoords + random(-speed, speed));
vertex(xCoords + random(-speed, speed), yCoords + random(-speed, speed));
where the coordinates are altered for successive vertices so that they are displayed in different places on the screen by adding or removing a number, in this case a random amount between -speed and speed. However, if we want a large number of vertices, this would become rather cumbersome and tedious to write. A loop statement would make this easier by simply looping for the number of vertices we want to create, such as
for (int i = 0; i < 100; i++) {
vertex(xCoords + random(-speed, speed), yCoords + random(-speed, speed));
}
This would create 100 vertices at random coordinates around the xCoords and yCoords values. The draw() method now looks like the following
void draw() {
// clear the screen by resetting the background
background(0);
// begin drawing the shape
beginShape();
// draw the vertex at the coordinates
for (int i = 0; i < 100; i++) {
vertex(xCoords + random(-speed, speed), yCoords + random(-speed, speed));
}
endShape();
// create a random direction for next time
float randomXDirection = random(-speed, speed);
float randomYDirection = random(-speed, speed);
// add the random direction to the existing coordinates
xCoords = xCoords + randomXDirection;
yCoords = yCoords + randomYDirection;
}
If you run this, you will see a 'sparkler' type effect shown in figure 41.3, similar to the effect we wanted, except that the whole animation moves, rather than individual points, and so it stays the same size.
Figure 41.3
As you can see, rather than the dots we started with, creating more than one vertex has created a solid, but complex, shape with little coding. This is because we have placed the vertex() function call between the beginShape() and endShape() functions. Doing this means that each vertex is taken as a point in a shape, and each vertex is joined up to create a single shape, rather than a set of dots. The shape is filled-in white because of the stroke(255) in the setup() method which tells Processing to create the shape white.
If each vertex were taken as a single shape, by putting beginShape() and endShape() inside the loop statement, each vertex would be drawn as a dot and a shape would not be created from all of the vertices together. This is the same effect that would be achieved by using point() rather than vertex(), which highlights the difference between the two functions.
To enable each vertex to act independently we need to be able to specify the coordinates of each vertex independently, rather than linking them all to being a random number from the one set of coordinates, which means that every vertex will move more-or-less together.
To do this we can create arrays containing the coordinates of each vertex. We will need two arrays, one to hold the x (horizontal) coordinates and one to hold the y (vertical) coordinates. Another, and possibly preferable, option would be to define the concept of a vertex's coordinates using a class – this is discussed later on page xx.
Our two arrays will need to be of equal size as each will be holding half the coordinates for a vertex. We can adapt our xCoords and yCoords variables to be arrays by adding the brackets
float[] xCoords;
float[] yCoords;
and adding a variable to contain the number of vertices we will be creating
int numberOfVertices;
We then need to specify the size of the arrays using the numberOfVertices
numberOfVertices = screenXSize;
xCoords = new float[numberOfVertices];
yCoords = new float[numberOfVertices];
This initialises the number of vertices to be the size of the screen's width, which we have found yields good results. The arrays are then declared as being of that size. Previously we set the coordinates to start with a value that places them in the middle of the screen. We will still need to do that but now we have a large number of coordinates to set, so we can use a loop to set the initial values
for (int i = 0; i < numberOfVertices; i++) {
xCoords[i] = screenXSize / 2;
yCoords[i] = screenYSize / 2;
}
This sets every float in the xCoords array to be the centre of the screen width, and every float in the yCoords array to be the centre of the screen height. This makes the setup() method look like the following
void setup() {
// initialise
screenXSize = 600;
screenYSize = 400;
speed = 2;
numberOfVertices = screenXSize;
xCoords = new float[numberOfVertices];
yCoords = new float[numberOfVertices];
// setup display variables
size(screenXSize, screenYSize);
background(0);
smooth();
strokeWeight(10);
stroke(255);
// initialise each vertex to be the middle of the display
for (int i = 0; i < numberOfVertices; i++) {
xCoords[i] = screenXSize / 2;
yCoords[i] = screenYSize / 2;
}
}
Now we need to access the coordinates of each vertex and draw it inside our loop by looping through the arrays and using the coordinates found there to create a vertex. As we now have the number of vertices stored in the numberOfVertices variable, we can use that in the loop to define the number of times it should loop
beginShape();
// for every vertex
for (int i = 0; i < numberOfVertices; i++) {
// draw the vertex at the coordinates
vertex(xCoords[i], yCoords[i]);
}
endShape();
The line
vertex(xCoords[i], yCoords[i]);
is the one that draws the vertex, using the value found in each array at position i. To begin with, every coordinate is set as the same value – the ones we set in the setup() method, so each vertex will be drawn on top of each other.
We also now need to alter our code for updating the coordinates each time the draw() method is run. As the coordinates are now held in arrays, rather than single float variables, we will need to update every value. This is where we have scope for altering each vertex independently in a random fashion.
Each time the a vertex is drawn inside the loop, we can also update the value of each coordinate so that the value is changed for the next time the vertex is drawn. If we use our previously created random coordinate calculation, we can update the value in the arrays by adding that random value to the coordinates currently held at that position in the array
float randomXDirection = random(-speed, speed);
float randomYDirection = random(-speed, speed);
// add the random direction to the existing coordinates
xCoords[i] = xCoords[i] + randomXDirection;
yCoords[i] = yCoords[i] + randomYDirection;
The lines at the bottom set the coordinate value to the old value and add the random value to it. As this is now set as the value in the array, the coordinates for that vertex will have changed next time it is drawn. As the random value will be different each time it is generated, the coordinates of each vertex will gradually become more different, and so move further away from the starting point. This means that the sparkler animation will appear to grow as the coordinates are updated.
Running the program, which should be the same as the very first listing, will show a small sparkler that gradually seems to grow and 'flicker'. If you want to speed up the animation you can add a call inside the setup() method to the frameRate() function that sets how many frames are displayed and therefore the speed at which the animation runs. The default is 60, so frameRate(100); will make it run faster
As a final point, the difference between using vertex() and point() is highlighted even more now that we have introduced this final piece of functionality using the arrays. Changing the vertex() function call to a point() function call
point(xCoords[i], yCoords[i]);
will display each point as a dot, except now each one is controlled independently using the arrays. If you make this change, something quite interesting happens – as each point is moving randomly, but starts at the same point, they appear to multiply from the centre a little like bacteria growth.
Figure 41.4 Bacteria growth animation

No comments:
Post a Comment