-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgravity.cc
180 lines (158 loc) · 4.8 KB
/
gravity.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#include <cmath>
#include <iostream>
#include <vector>
#include <boost/math/constants/constants.hpp>
using boost::math::constants::pi;
#include <boost/multiprecision/cpp_dec_float.hpp>
typedef boost::multiprecision::cpp_dec_float_50 Real;
const Real G = 6.67430e-11; // m^3 kg^-1 s^-2
const int total_steps = 1000000;
const int steps_per_print = total_steps / 100;
/**
* An entity in a 2D space.
* @note The entity has a mass, position, velocity, and acceleration.
* @note The entity is affected by gravity.
*/
class Entity
{
public:
Entity(Real mass, Real x, Real y)
: mass(mass), x(x), y(y) {}
/**
* Update the entity's position and velocity.
* @param dt The time step.
* @note The entity's position is updated using the formula:
* x = x + v_x * dt
* y = y + v_y * dt
* @note The entity's velocity is updated using the formula:
* v_x = v_x + a_x * dt
* v_y = v_y + a_y * dt
* @note The entity's acceleration is reset to zero after updating the velocity.
*/
void Update(Real dt)
{
v_x += a_x * dt;
v_y += a_y * dt;
x += v_x * dt;
y += v_y * dt;
a_x = 0;
a_y = 0;
}
Real const mass;
Real x;
Real y;
Real v_x = 0;
Real v_y = 0;
Real a_x = 0;
Real a_y = 0;
};
/**
* Output an entity.
* @param os The output stream.
* @param e The entity.
* @return The output stream.
* @note The entity is output in the format:
* p<x>,<y>, v<speed>∠<angle>
* @note The angle is in degrees.
* @note The angle is measured counter-clockwise from the x-axis.
*/
std::ostream &operator<<(std::ostream &os, Entity const &e)
{
auto speed = sqrt(e.v_x * e.v_x + e.v_y * e.v_y);
auto angle = atan2(e.v_y, e.v_x) * 180 / pi<Real>();
os << 'p' << e.x << ',' << e.y
<< ", v" << speed << "∠" << angle;
return os;
}
/**
* Output a vector of entities.
* @param os The output stream.
* @param entities The vector of entities.
* @return The output stream.
* @note The entities are output in the format:
* p<x>,<y>, v<speed>∠<angle> p<x>,<y>, v<speed>∠<angle> ...
* @note The entities are separated by two spaces.
*/
std::ostream &operator<<(std::ostream &os, std::vector<Entity> const &entities)
{
auto space = "";
for(auto const &e : entities)
{
os << space << e;
space = " ";
}
return os;
}
/**
* Calculate the force of attraction between two entities due to gravity.
* @param a The first entity.
* @param b The second entity.
* @return The force of attraction between the two entities.
* @note The force is always attractive.
* @note The force is calculated using Newton's law of universal gravitation.
* @note The force is calculated using the formula:
* F = G * m1 * m2 / r^2
*/
Real Get_attraction(Entity const &a, Entity const &b)
{
auto dx = b.x - a.x;
auto dy = b.y - a.y;
auto d_squared = dx * dx + dy * dy;
auto f = G * a.mass * b.mass / d_squared;
return f;
}
/**
* Add the force of attraction between two entities due to gravity.
* @param entity_1 The first entity.
* @param entity_2 The second entity.
* @param force The force of attraction between the two entities.
* @note The force is always attractive.
* @note The force is added to the acceleration of each entity.
* @note The force is added using Newton's second law of motion.
* @note The force is added using the formula:
* F = m * a
* a = F / m
*/
void Add_force(Entity &entity_1, Entity &entity_2, Real force)
{
auto dx = entity_1.x - entity_2.x;
auto dy = entity_1.y - entity_2.y;
auto theta = atan2(dy, dx);
auto f_x = force * cos(theta);
auto f_y = force * sin(theta);
entity_1.a_x -= f_x / entity_1.mass;
entity_1.a_y -= f_y / entity_1.mass;
entity_2.a_x += f_x / entity_2.mass;
entity_2.a_y += f_y / entity_2.mass;
}
int main(int argc, char *argv[])
{
std::vector<Entity> entities;
entities.push_back(Entity(1, -1, 0));
entities.push_back(Entity(1, 1, 0));
entities.push_back(Entity(2, 1, 1));
std::cout << entities << std::endl;
for (int i = 0; i < total_steps; ++i)
{
// Loop over all pairs of entities and calculate the force between them due to gravity.
// Then add the force to each entity.
for(auto outer = entities.begin(); outer != entities.end(); ++outer)
{
for(auto inner = entities.begin(); inner != outer; ++inner)
{
auto force = Get_attraction(*outer, *inner);
Add_force(*inner, *outer, force);
}
}
for(auto &e : entities)
{
e.Update(0.01);
}
if (i % steps_per_print == 0)
{
std::cout << entities << std::endl;
}
}
std::cout << entities << std::endl;
return 0;
}