Skip to content

Position and movement

Hannupekka Sormunen edited this page Dec 30, 2018 · 6 revisions

Table of Contents

  1. Position
  2. Movement
  3. Building a particle class and using radian values for direction

Position

In the Particle Fire Simulation Revision positioning of each particle is done using Cartesian coordinate system. Even though the program itself uses SDL-library, the concepts presented here are not depended on the graphics library. Any graphics library, that has ability to do pixel access, can be used.

So let's get going. Here is an graph of coordinates used in this program: Cartesian coordinate system

Both the Y-axis (the line in the middle that goes from top to bottom) and the X-axis (the line in the middle that goes from left to right) are in range of 1 to -1 where the topmost value of Y-axis’s is 1 and the rightmost value is X-axis’s is 1. While -1 being Y-axis’s bottom-most value and X-axis’s leftmost value.

To further understand how each particle is positioned on the screen using these coordinates, this example will demonstrate how twenty particles are positioned in random positions. This can be achieved by using following algorithm for setting position to both Y- and X-axis:

//Random position for X-axis:
double particle_xpos = ((2.0 * rand()) / RAND_MAX) - 1;
//Random position for Y-axis:
double particle_ypos = ((2.0 * rand()) / RAND_MAX) - 1;

Where rand is a pseudo-random integral value between ​0​ and 32767, thus RAND_MAX being the maximum value 32767. In short random number between 0 and 32767 is divided by 32767. After that the result is multiplied by 2.0 so that the final outcome is converted to floating point numbers and then one last -1 so that the values range on 1 and -1 according the coordinate system presented above. Here is an example of of twenty particle object’s (elements ranging from 0 to 19) position calculated with above algorithm:

Particle # X-axis Y-axis
0 0.881324 0.0454907
1 -0.641283 0.918335
2 0.806671 -0.941154
3 -0.141915 0.273706
4 -0.201072 0.40221
5 0.776629 0.448826
6 -0.497708 -0.82714
7 0.22043 0.314566
8 0.286918 0.880185
9 -0.693276 -0.391165
10 -0.715079 -0.851693
11 -0.494808 0.0400419
12 -0.0897969 -0.306577
13 0.216423 -0.523715
14 0.274763 0.94553
15 -0.461073 0.393439
16 -0.00897887 -0.102355
17 0.311774 -0.202308
18 -0.0435098 -0.830141
19 -0.928602 0.755418

As can be seen none of the values go over the 1 to -1 range. To further demonstrate how each of the particles are set, here is an graph that displays their position on the coordinate system: Cartesian coordinate system, dotted

Now, to change these coordinate positions to pixel position on the screen one more algorithm needs to be executed:

int pixel_x = (particle_xpos + 1) * SCREEN_WIDTH / 2;
int pixel_y = (particle_ypos + 1) * SCREEN_HEIGHT / 2;

Let’s assume that resolution width is 1280 and height 720, when previous coordinates are put through previous algorithm values are changed to following:

Particle # X-axis Y-axis
0 76 376
1 230 691
2 1156 21
3 549 459
4 511 505
5 1137 522
6 321 62
7 781 473
8 824 677
9 196 219
10 182 53
11 323 374
12 583 250
13 779 171
14 816 700
15 345 502
16 634 323
17 840 287
18 612 61
19 46 632

So how the coordinate to resolution values algorithm works? First, 1 is added to values in both variable particle_xpos and particle_xpos, because when turning coordinates from range of 1 to -1 to resolution pixel values, there cannot be negative numbers, since those values would go out of the screen. With this little addition values are changed so that they are converted to range of 2 to 0. After that they are multiplied by screen width or height depending on the axis and finally divided by two to again make sure that the values are within resolution range.

For last example, going over how the whole positioning works, by selecting one random number between 0 and 32767, generator gave me number 12226. Let's say that this is the x-axis of resolution of 1920 in width and 1080 in height. Value 12226 is then divided by RAND_MAX 32767, which equals to 0.3731192968535417. This is then multiplied by 2 resulting 0.7462385937070833. Finally we reduce it by 1 resulting -0.2537614062929167. This is the coordinate on the Cartesian coordinate system. To change this to resolution value, 1 is added solution of first algorithm resulting again 0.7462385937070833. This is then multiplied by 1920 resulting 1432.7780999176 and finally divided by 2, which in turn is 716.3890499588. Since value is stored in the integer variable the decimals after the dot are discarded and the final value is 716 in x-axis. Now the same can be repeated to Y-axis, but of course that would need another random number and the conversion to pixel is done with 1080 rather than 1920.

Movement

Understanding how particles are placed on the coordinate system, so how about making particles move utilizing the very same system? This can be done by adding variables to horizontal and vertical movement of particles. Basically when movement is done particle’s position value is changed so subtly that it seems to human eye that it appears to be moving from one part of the screen to another. What is really happening is that when new frame is drawn on the screen. Think of it as drawing an animation on couple of pages on a flipbook where the pages change so suddenly that it seems like movement. Like with flipbook, we are at the basics of animation here with particle movement also. The particle X- and Y- axis position is ever slightly changed so that it is seems like smooth movement when the screen updated in 30 or 60 times per second. Algorithm for calculating new position begins by getting values by which the particle is moving, the so called speed:

//Calculate the movement speed on X-axis
particle_xSpeed = 0.001 * (((2.0 * rand())/RAND_MAX) -1);
//Calculate the movement speed on Y-axis
particle_ySpeed = 0.001 * (((2.0 * rand())/RAND_MAX) -1);

There is one part (2.0 * rand())/RAND_MAX) -1) that is the same algorithm as setting the position of particle, this is done because otherwise values would be totally different and the particle’s movement would not smooth, but rather uneven, even jumping from top of the screen to the bottom in one millisecond or being out of bounds of the grid. New position must still be inside the 1 to -1 range that is set on the coordinate system used here. To set a new particle position relatively close to the old one, the speed algorithm has multiplication by 0.001 on both axises.

Here is an example speeds of of twenty particle objects (elements ranging from 0 to 19) calculated with algorithm presented previously:

Particle # X-axis Y-axis
0 0.00113254 0.000878026
1 0.00197642 0.000266854
2 -0.00199384 -0.000478084
3 0.00176537 0.00162978
4 0.0018651 0.00160843
5 0.00141587 0.000594453
6 -0.000914855 -0.00129146
7 0.00184443 0.00186402
8 0.00101401 0.000623127
9 -0.00111002 0.00043069
10 -0.00195261 -0.000935308
11 -0.00130553 -0.00146449
12 -0.00185605 0.00109067
13 -0.000314882 0.000458249
14 0.00116679 -0.000440001
15 -0.000304019 0.00107844
16 -0.000298437 0.00131639
17 -0.000252924 0.00148801
18 -0.0014473 -0.00113466
19 -0.00059915 0.000740988

As can be seen these values are rather small, but that is really the purpose as said, rapid movement doesn’t look smooth to human eye. But how these speed values are used to move the particles around? Answer is simply adding them to particle’s position like so:

//Set new position on the screen for X-axis
particle_xpos += particle_xSpeed;
//Set new position on the screen for Y-axis
particle_ypos += particle_ySpeed;

To demonstrate this, particle #8 have selected, which is positioned at 0.286918 on x-axis and at 0.880185 on y-axis. For speed particle #2’s speed have been selected for this example. So particle #8 will travel at speed of -0.00199384 on x-axis and at -0.000478084 y-axis. Here is what it looks like on table when the position is upgraded 19 times:

Update # X-axis Y-axis
1 0.286918 0.880185
2 0.28492416 0.879706916
3 0.28293032 0.879228832
4 0.28093648 0.878750748
5 0.27894264 0.878272664
6 0.2769488 0.87779458
7 0.27495496 0.877316496
8 0.27296112 0.876838412
9 0.27096728 0.876360328
10 0.26897344 0.875882244
11 0.2669796 0.87540416
12 0.26498576 0.874926076
13 0.26299192 0.874447992
14 0.26099808 0.873969908
15 0.25900424 0.873491824
16 0.2570104 0.87301374
17 0.25501656 0.872535656
18 0.25302272 0.872057572
19 0.25102888 0.871579488
20 0.24903504 0.871101404

Here is that same pattern in graph format, starting from top right and final update being on the lower left: Cartesian coordinate system, movement

Even if this works and it can be used, doesn’t mean it is completely efficient. In next header, class for particle will be build and make the code even more elegant. Do also notice, that if there is nothing removing the old particle position from the screen after updating the position then rather than single particle moving from one position to another, a line is drawn like in the example grid above. There are many methods for doing that and one of them is blurring that is also presented on this program. Another possible solution is also to use graphics library function to draw a blank screen after every position change.

Building a particle class and using radian values for direction

According to what we have learned so far, a particle class would look like this:

class Particle {
	//Data members
private:
	double particle_xpos;
	double particle_ypos;

	double particle_xSpeed;
	double particle_ySpeed;

//Data methods
public:
	//Constructor that sets particle in random position and defines random speed for them
	Particle(){
particle_xpos = ((2.0 * rand()) / RAND_MAX) - 1;
	particle_ypos = ((2.0 * rand()) / RAND_MAX) - 1;

	particle_xSpeed = 0.001 * (((2.0 * rand()) / RAND_MAX) - 1);
	particle_ySpeed = 0.001 * (((2.0 * rand()) / RAND_MAX) - 1);
}
	//Data method that updates particle position according the speed value
	void update(){
particle_xpos += particle_xSpeed;
	particle_ypos += particle_ySpeed;
}

}; //end of class definition

When an object is created from this class, a constructor sets values to random using the same algorithms described on previous header. After that each movement of the particle can be done with update data method, and like above it can be done 19 times or as long as particle is no longer even visible on the coordinate grid. To keep this code simple there is nothing preventing values going over 1 or -1, but implementing such “guard” is out of scope of this example. Now because updating both the X and Y-axis speed using seperate speed algorithms is not efficient, there is a way to make one separate variable for speed and another for direction. But how can be degrees used for setting direction?

Answer for that is to set direction is using something called radian, it is a system where 360 degrees is same as 2 multiplied by pi. Since pi is 3.14159265358979323846 then the 6.28318530717958647692 equals to 360 degrees in radian system. More about how radian works can be found here. Radian system is used in mathematical situations like this because it is more flexible than just using regular degrees. Algorithm for random direction for each particle would be:

particle_direction = (2 * M_PI * rand()) / RAND_MAX

Where M_PI is value of pi with 20 digits as stated above, rand() and RAND_MAX the same as before when defining random values for position and speed. And yet again here is 20 particle objects with each their own directions calculated with this algorithm and for reference also presented in degrees:

Particle # Radians Degrees
0 3.47482 199.09252057°
1 1.59662 91.479587486°
2 6.1747 353.78424976°
3 6.05705 347.0434013°
4 1.83014 104.85929792°
5 3.01972 173.01721131°
6 1.65155 94.626844655°
7 2.99092 171.36709286°
8 5.97048 342.08330567°
9 3.73754 214.14526776°
10 4.12701 236.46025501°
11 2.15034 123.20540652°
12 5.42058 310.57635651°
13 5.14657 294.87673997°
14 3.18864 182.69561439°
15 3.31029 189.66564596°
16 1.86675 106.95689641°
17 3.4352 196.82246178°
18 2.83417 162.38597942°
19 0.100518 5.7592571651°

For example particle #0 is travelling to 3.47482 radians which is 199.09252057 degrees. This can calculated by this function: degrees = α(radians) × 180 degrees / pi. So how are these radians converted into travelling motion on the screen? First updating particle class to include radian directions:

class Particle {
	//Data members
private:
	double particle_xpos;
	double particle_ypos;

	double particle_speed;
	double particle_direction;

//Data methods
public:
//Constructor that sets particle in random position and defines random speed and direction for them
	Particle(){
particle_xpos = ((2.0 * rand()) / RAND_MAX) - 1;
	particle_ypos = ((2.0 * rand()) / RAND_MAX) - 1;

particle_direction = (2 * M_PI * rand()) / RAND_MAX;
	particle_speed = (0.001 * rand()) / RAND_MAX;

}
	//Data method that updates particle position according the speed value
	void update(){
	double calculateXspeed = particle_speed * cos(particle_direction);
	double calculateYspeed = particle_speed * sin(particle_direction);

	particle_xpos += calculateXspeed;
	particle_ypos += calculateYspeed;
}

}; //end of class definition

Going over how the new update class works. Instead of having two different data members for speed in X- and Y-axis now there is single data member for speed and one for direction. And since there is no longer need to use negative speed values to go backwards on a grid, -1 can be also discarded from the previous algorithm. Update method has changed from just updating particle position depending on the speed data member, to calculating how fast particle is moving in relation to both x- and x-axis. To further demonstrate cos and sin functions work and what values do they return, here is picture for starters: Cartesian coordinate system, cos and sin To recap the center point is at (X:0, Y:0). The circle drawn on picture above has a radius of 1, so the rightmost point of the circle is (X:1, Y:0), the top is (X:0, Y:1), the leftmost is (X:-1, Y:0), and the bottom is (X:0, Y:-1). Do notice however, that this graph differs from Cartesian coordinate system graph earlier, since in this graph’s center point represents a particle’s center, rather than center of a screen. Also do remember, that when using unit circle 0 rad is at rightmost position and that also these rules of thumb apply:

  • 0 degrees or 0 rad is at rightmost position, in coordinates X:1 and Y:0.
  • 90 degrees or 0.5 * pi rad is at topmost position, in coordinates X:0 and Y:1.
  • 180 degrees or pi rad is at leftmost position, in coordinates X:-1 and Y:0.
  • 270 degrees or 1.5 * pi rad is at bottom-most position, in coordinates X:0 and Y:-1.
  • After full circle 360 degrees is 2 * pi rad, again at rightmost position and coordinates X:1 and Y:0.

So 0.5 * pi, pi ..etc. are in relation to rightmost position on x-axis. So what does functions cos and sin do? In short they return the sine and cosine of an angle of radians inputted to them in range of 1 to -1. Cos is used to calculate x-axis and sin to y-axis from radian. If we take the values from rules of thumb above and convert them to angles here are the results in same order:

  • cos( 0 ) = 1, sin( 0 ) = 0
  • cos( 0.5 * pi ) = 0, sin( 0.5 * pi ) = 1
  • cos( pi ) = -1, sin( pi ) = 0
  • cos( 1.5 * pi ) = 0, sin( 1.5 * pi ) = -1
  • cos( 2 * pi ) = 1, sin( 2 * pi ) = 0

To further illustrate this, let's continue using particle_direction data member with a value of 3.47482 radian, which is 199.09252057 degrees. With this value cos(particle_direction) returns value -0.94499162 on X-axis and sin(particle_direction) returns value -0.32709454 on Y-axis. Positioning these values inside the coordinates it would mean that particle would move to left and slightly down from its current position: Cartesian coordinate system, cos and sin, result

Here is an example run of very fast (with 0.01 multiplier to speed and random generated number of 24864) of the particle moving to 3.47482 radian, and for the sake of an example, from middle of the of the grid (position X:0, Y:0).

Update # X-axis Y-axis
0 0 0
1 -0.007170711886862 -0.002482033339201
2 -0.014341423773724 -0.004964066678402
3 -0.021512135660585 -0.007446100017603
4 -0.028682847547447 -0.009928133356804
5 -0.035853559434309 -0.012410166696005
6 -0.043024271321171 -0.014892200035206
7 -0.050194983208033 -0.017374233374407
8 -0.057365695094894 -0.019856266713608
9 -0.064536406981756 -0.022338300052809
10 -0.071707118868618 -0.02482033339201
11 -0.07887783075548 -0.027302366731211
12 -0.086048542642342 -0.029784400070412
13 -0.093219254529203 -0.032266433409613
14 -0.100389966416065 -0.034748466748814
15 -0.107560678302927 -0.037230500088015
16 -0.114731390189789 -0.039712533427216
17 -0.12190210207665 -0.042194566766418
18 -0.129072813963512 -0.044676600105619
19 -0.136243525850374 -0.04715863344482
20 -0.143414237737236 -0.049640666784021

And finally example in drawn to grid, in this example grid is coordinate graph that displays where the particle moves on the screen:: Cartesian coordinate system, cos and sin

To see how this is implemented on this project please check out Particle.h and Particle.cpp on this wiki or straight from source code at repo page.