-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgallery_particle_physics_sim.cpp
More file actions
227 lines (199 loc) · 7.22 KB
/
Copy pathgallery_particle_physics_sim.cpp
File metadata and controls
227 lines (199 loc) · 7.22 KB
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/**
* @file gallery_particle_physics_sim.cpp
* @brief Interactive Particle Physics Simulation with Real-Time Animation
* @author plotly.cpp contributors
* @date 2025
*
* @example gallery_particle_physics_sim.cpp
* This gallery example demonstrates an advanced real-time physics simulation
* using Plotly.cpp. It simulates charged particles with electromagnetic
* interactions, collision detection, and realistic physics behavior in a
* bounded environment.
*
* Features demonstrated:
* - Real-time particle physics simulation with Coulomb force calculations
* - Dynamic scatter plot animation using plot updates
* - Multi-threaded simulation loop with controlled frame rate
* - Interactive visualization with hover information showing particle
* properties
* - Particle size mapping to mass and color mapping to electric charge
* - Boundary collision detection with energy damping
* - Scientific color scale for charge visualization with colorbar
*
* Physics concepts implemented:
* - Coulomb's law for electromagnetic force calculation (F = k·q₁·q₂/r²)
* - Newton's laws of motion for particle dynamics (F = ma)
* - Energy damping for realistic particle behavior
* - Elastic collision with boundary walls
* - Multi-particle interaction system with N-body calculations
*
* The simulation creates a visually engaging demonstration of electrodynamics
* with particles that attract/repel based on their charges and exhibit
* realistic motion patterns within a confined space.
*
* @image html particle_physics_sim.gif "Interactive Particle Physics
* Simulation"
*
*/
#include "plotly/plotly.hpp"
#include <algorithm>
#include <chrono>
#include <cmath>
#include <iostream>
#include <random>
#include <string>
#include <thread>
#include <vector>
struct Particle {
double x, y;
double vx, vy;
double mass;
double charge;
std::string color;
};
auto main() -> int {
std::cout << "Starting particle physics simulation..." << '\n';
plotly::Figure fig;
fig.openBrowser();
// Simulation parameters
const int numParticles = 30;
const double dt = 0.02;
const double boxSize = 10.0;
const double dampening = 0.999;
const int animationFrames = 200;
const int frameDelay = 50; // milliseconds
// Initialize random particles
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<double> posDist(-boxSize / 2, boxSize / 2);
std::uniform_real_distribution<double> velDist(-2.0, 2.0);
std::uniform_real_distribution<double> massDist(0.5, 2.0);
std::uniform_real_distribution<double> chargeDist(-1.0, 1.0);
std::vector<std::string> colors = {"red", "blue", "green", "orange",
"purple", "cyan", "magenta", "yellow",
"brown", "pink"};
std::vector<Particle> particles(numParticles);
for (int i = 0; i < numParticles; i++) {
particles[i] = {
.x = posDist(gen),
.y = posDist(gen), // position
.vx = velDist(gen),
.vy = velDist(gen), // velocity
.mass = massDist(gen), // mass
.charge = chargeDist(gen), // charge
.color = colors[i % colors.size()] // color
};
}
// Create initial particle positions
std::vector<double> xPos, yPos, sizes, charges;
std::vector<std::string> particleColors;
for (const auto &p : particles) {
xPos.push_back(p.x);
yPos.push_back(p.y);
sizes.push_back(p.mass * 20); // Scale size by mass
charges.push_back(p.charge);
particleColors.push_back(p.color);
}
// Create initial trace
plotly::Object trace = {
{"type", "scatter"},
{"mode", "markers"},
{"x", xPos},
{"y", yPos},
{"marker",
{{"size", sizes},
{"color", charges},
{"colorscale", "RdBu"},
{"showscale", true},
{"colorbar", {{"title", "Electric Charge"}, {"titleside", "right"}}},
{"line", {{"width", 2}, {"color", "black"}}}}},
{"name", "Particles"},
{"hovertemplate", "Position: (%{x:.2f}, %{y:.2f})<br>" +
std::string("Charge: %{marker.color:.2f}<br>") +
"Mass: %{marker.size:.0f}<extra></extra>"}};
// Create layout
plotly::Object layout = {
{"title",
{{"text", "Particle Physics Simulation<br>" +
std::string("<sub>Charged particles with electromagnetic "
"interactions</sub>")},
{"font", {{"size", 16}}}}},
{"xaxis",
{{"title", "X Position"},
{"range", {-boxSize, boxSize}},
{"showgrid", true},
{"zeroline", true}}},
{"yaxis",
{{"title", "Y Position"},
{"range", {-boxSize, boxSize}},
{"showgrid", true},
{"zeroline", true},
{"scaleanchor", "x"}}},
{"width", 800},
{"height", 700},
{"showlegend", false}};
// Create initial plot
std::vector<plotly::Object> data = {trace};
fig.newPlot(data, layout);
std::cout << "Starting animation with " << numParticles << " particles..."
<< '\n';
// Physics simulation loop
for (int frame = 0; frame < animationFrames && fig.isOpen(); frame++) {
// Calculate forces and update particles
for (int i = 0; i < numParticles; i++) {
double fx = 0.0, fy = 0.0;
// Calculate electromagnetic forces from other particles
for (int j = 0; j < numParticles; j++) {
if (i == j)
continue;
double dx = particles[j].x - particles[i].x;
double dy = particles[j].y - particles[i].y;
double r2 = dx * dx + dy * dy;
double r = std::sqrt(r2);
if (r > 0.1) { // Avoid division by zero
// Coulomb force: F = k * q1 * q2 / r^2
double forceMagnitude =
particles[i].charge * particles[j].charge / r2;
fx -= forceMagnitude * dx / r; // Repulsive if same charge
fy -= forceMagnitude * dy / r;
}
}
// Update velocity with force (F = ma, so a = F/m)
particles[i].vx += fx / particles[i].mass * dt;
particles[i].vy += fy / particles[i].mass * dt;
// Apply dampening
particles[i].vx *= dampening;
particles[i].vy *= dampening;
// Update position
particles[i].x += particles[i].vx * dt;
particles[i].y += particles[i].vy * dt;
// Bounce off walls
if (particles[i].x < -boxSize / 2 || particles[i].x > boxSize / 2) {
particles[i].vx *= -0.8;
particles[i].x =
std::max(-boxSize / 2, std::min(boxSize / 2, particles[i].x));
}
if (particles[i].y < -boxSize / 2 || particles[i].y > boxSize / 2) {
particles[i].vy *= -0.8;
particles[i].y =
std::max(-boxSize / 2, std::min(boxSize / 2, particles[i].y));
}
}
// Update plot data
xPos.clear();
yPos.clear();
for (const auto &p : particles) {
xPos.push_back(p.x);
yPos.push_back(p.y);
}
// Update the plot
fig.restyle({{"x", {xPos}}, {"y", {yPos}}}, {0});
std::this_thread::sleep_for(std::chrono::milliseconds(frameDelay));
if (frame % 50 == 0) {
std::cout << "Frame " << frame << "/" << animationFrames << '\n';
}
}
std::cout << "Simulation completed. Close browser to exit." << '\n';
fig.waitClose();
return 0;
}