 |
       |
| |
Part III
|
|
Simple system - particle system |
|
L-system and turtle graphics |
|
Complex system - cellular automata |
|
| |
| Background knowledge |
Going back to our ball example many years ago, we try to establish the common ground for all to start with. Here is the exercise 1. Use Processing to draw a circle in the middle of the screen.
Use variables x and y to maintain the x and y location of the centre of the ball, r for its radius. You can choose whatever colour you want by using the fill() command.
float x, y;
int r;
void setup() {
size(400,400);
smooth();
noStroke();
x = width/2;
y = height/2;
r = 10;
}
void draw() {
background(0);
fill(255,100,0);
ellipse(x,y,r*2,r*2);
} |

|
| |
| Animation |
If we add another pair of variables: vx, vy, we can change the values of x, y by incrementing them by vx, vy. The ball can appear to move.
float x, y;
float vx, vy;
int r;
void setup() {
size(400,400);
smooth();
noStroke();
x = width/2;
y = height/2;
vx = 1.0;
vy = 1.0;
r = 10;
}
void draw() {
background(0);
fill(255,100,0);
x += vx;
y += vy;
ellipse(x,y,r*2,r*2);
} |
|
| Can I have more balls? |
The inital thought is that we can use more variables to represent individual balls to be shown on screen. For example,
x1, y1, vx1, vy1
x2, y2, vx2, vy1
etc.
But it is not a good solution if we have hundreds of balls.
|
| Object revisited |
We'll use the object facility to handle it. First, we define a class - Ball, which is similar to a family. Each member of the family Ball will be an instance of the class.
class Ball { float x, y; float vx, vy; int r; Ball(float _x, float _y) { x = _x; y = _y; vx = 1.0; vy = 1.0; r = 10; } void update() { x += vx; y += vy; render(); } void render() { fill(255,200,0); ellipse(x,y,r*2,r*2); } } |
- The class command defines the Ball as an object family.
- The Ball function is called a constructor of the class which we use to initialize each member of the class.
- The function update() helps to calculate the location at each frame.
- The function render() draws the ball as a colour circle.
To make use of the class, we have the main program here.
Ball b1;
void setup() {
size(300,300);
smooth();
noStroke();
b1 = new Ball(width/2,height/2);
}
void draw() {
background(0);
b1.update();
} |
Note the simplicity of the main program. In case we need to add more balls, we can just add another variables b2, b3, etc. A more effective way will be using an array of the object Ball.
final int MAX = 10;
Ball [] balls = new Ball[MAX];
void setup() {
size(300,300);
smooth();
noStroke();
for (int i=0;i<MAX;i++) {
balls[i] = new Ball(random(100,200),random(100,200));
}
}
void draw() {
background(0);
for (int i=0;i<MAX;i++) {
balls[i].update();
}
} |
|
| |
| Particle |
Particle or particle system is similar to a group of balls. In most case, we would like to adopt a life metaphor and give a life span for each particle. It appears upon some interactions and disappears when it exceeds its life span.
For some particle systems, the particles do not vanish, like the original Karl Sims' animation.
In order for us to simulate a life cycle, the use of array may not be a good choice as it is not efficient to dynamically add and remove entries in the array.
We are going to use a new data structure from Java - ArrayList.
First we create an empty ArrayList - balls. When we create a new ball, we append an entry. When a ball expires, it is removed from the ArrayList.
Create an empty ArrayList
ArrayList balls = new ArrayList(); |
Add a new ball to the ArrayList
balls.add(new Ball(width/2, height/2)); |
Remove the i-th ball from the ArrayList
int i = ?;
balls.remove(i); |
Remove everything from the ArrayList
Get the i-th ball from the ArrayList
Ball b = (Ball) balls.get(i); |
Get the total number of balls in the ArrayList
|
| ArrayList implementation |
Replace the previous example by using an ArrayList in the main program.
final int MAX = 10;
ArrayList balls;
void setup() {
size(300,300);
smooth();
noStroke();
balls = new ArrayList();
for (int i=0;i<MAX;i++) {
balls.add(new Ball(random(100,200),random(100,200)));
}
}
void draw() {
background(0);
int cnt = balls.size();
for (int i=0;i<cnt;i++) {
Ball b = (Ball) balls.get(i);
b.update();
}
} |
When we need to remove balls from the ArrayList, sometimes, it is better to go through the list from the higher number index to lower, i.e. loop from the balls.size()-1 back to 0. It will be explained later.
for (int i=cnt-1;i>=0;i--) {
Ball b = (Ball) balls.get(i);
b.update();
} |
|
| Giving life to the balls |
In this version, we give life to the balls. There will be 2 more variables in the Ball class.
int life;
boolean active; |
In the constructor, we initialize them with proper values. Each ball has a random value for life between 100 to 200. And each is active in the beginning.
life = int(random(100,200));
active = true; |
In the update() function, we decrement life by 1 each time. And we also check if life is less than 0 or not. If it is less than 0, the ball changes to inactive and there will be no need to update an inactive ball.
if (active) { x += vx; y += vy; life--; if (life<0) { active = false; } render(); } |
|
| Remove inactive balls |
In the main program, we remove the inactive balls such that we can continue to add more without using up the system memory. In the draw() function, we have,
int cnt = balls.size(); for (int i=cnt-1;i>=0;i--) { Ball b = (Ball) balls.get(i); if (b.active) { b.update(); } else { balls.remove(i); } } |
Starting from this moment, we can introduce interactivity to create a new ball dynamically. And it will be much interesting to have each ball random movement direction. Think about how you can achieve it.
To add a new ball by pressing the mouse, we put the following,
if (mousePressed) { balls.add(new Ball(width/2, height/2)); } |
|
| A complete example |
Note the use of random colour, vx and vy in the Ball object. For each frame of the life of a ball, its alpha also decreases.
|
| Variations |
If we change the range of values for vx, vy and the initial position, we can simulate natural events.
Modify the above program such that it does not draw the ellipse directly but displays a bitmap image and its mask.
You can have a more realistic result.
|
| More professional |
A more professional way is to use another object class to manage / control the Ball objects. The main program will be much simpler.
We create a new class as a particle system (PartSystem) and put the maintenance part from the main program into the class.
class PartSystem {
ArrayList balls;
PartSystem() {
balls = new ArrayList();
}
void add(float _x, float _y) {
balls.add(new Ball(_x, _y));
}
void update() {
int cnt = balls.size();
for (int i=cnt-1;i>=0;i--) {
Ball b = (Ball) balls.get(i);
if (b.active) {
b.update();
} else {
balls.remove(i);
}
}
}
} |
Write your own main program as an exercise to use this newly defined class.
|
| The force |
At the moment, all the particles travel in space without being attracted by any forces. The following exercises apply forces to the particle / ball such that it changes its velocity in time. According to Newton, force determines the acceleration. In order not to increase the velocity infinitely, we add a damping factor to cater for the air friction or drag force.
The first one is gravity. The attractive force due to gravity will always direct downward, i.e. in positive direction of y. A new pair of variables will be added to the class Ball, together with a damping factor.
float ax, ay;
float damp; |
void update() { if (active) { forces(); vx += ax; vy += ay; vx *= damp; vy *= damp; x += vx; y += vy; life--; int ap = int(alpha(c)); ap = max(0,ap-2); c = color(red(c),green(c),blue(c),ap); if (life<0) { active = false; } render(); } } |
void forces() {
ay = 0.1;
} |
|
| Varying forces |
Gravity is a kind of fixed force. In the following example, we introduce a force that changes according to its distance with the mouse, like elasticity.
void forces() {
float dd = dist(mouseX,mouseY,x,y);
float da = atan2(mouseX-x,mouseY-y);
ax = dd*cos(da)*0.003;
ay = dd*sin(da)*0.003;
} |
|
| Application |
If we draw a line connecting all the previous x, y to the current x, y of a ball, we can have the following animation.
|