ΠΡ ΡΠΈΠΌΡΠ»ΠΈΡΡΠ΅ΠΌ Π³ΡΠ°Π²ΠΈΡΠ°ΡΠΈΠΎΠ½Π½ΠΎΠ΅ Π²Π·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡΠ²ΠΈΠ΅ 100 ΡΠ°ΡΡΠΈΡ Π² 2D-ΠΏΡΠΎΡΡΡΠ°Π½ΡΡΠ²Π΅, ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ Π°Π»Π³ΠΎΡΠΈΡΠΌ N-Body Ρ ΠΎΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΠ΅ΠΉ ΠΠ°ΡΠ½ΡΠ°-Π₯Π°Ρ!
Π‘ΠΈΠΌΡΠ»ΡΡΠΈΡ Π³Π°Π»Π°ΠΊΡΠΈΠΊΠΈ:
cpp
#include <avr/random.h>
#include <avr/pgmspace.h>
#define PARTICLE_COUNT 64
#define GRAVITY 10
#define SOFTENING 2
#define TIME_STEP 1
// Π‘ΡΡΡΠΊΡΡΡΠ° ΡΠ°ΡΡΠΈΡΡ
struct Particle {
int16_t x;
int16_t y;
int16_t vx;
int16_t vy;
int16_t mass;
};
Particle particles[PARTICLE_COUNT];
// ΠΠ²Π°Π΄ΡΠΎΠ΄Π΅ΡΠ΅Π²ΠΎ (Π΄Π»Ρ ΠΎΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΠΈ Barnes-Hut)
struct QuadNode {
int16_t x, y; // Π¦Π΅Π½ΡΡ ΠΌΠ°ΡΡ
int16_t mass; // Π‘ΡΠΌΠΌΠ°ΡΠ½Π°Ρ ΠΌΠ°ΡΡΠ°
int16_t minX, maxX; // ΠΡΠ°Π½ΠΈΡΡ
int16_t minY, maxY;
uint8_t childCount;
QuadNode* children[4]; // ΠΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ ΠΈΠ½Π΄Π΅ΠΊΡΡ Π²ΠΌΠ΅ΡΡΠΎ ΡΠΊΠ°Π·Π°ΡΠ΅Π»Π΅ΠΉ
};
QuadNode nodes[PARTICLE_COUNT * 4];
uint8_t nodeCount = 0;
// Π‘ΡΡΠΎΠΈΠΌ ΠΊΠ²Π°Π΄ΡΠΎΠ΄Π΅ΡΠ΅Π²ΠΎ
void buildQuadTree(uint8_t nodeIdx, uint8_t* particleList, uint8_t count) {
QuadNode* node = &nodes[nodeIdx];
// ΠΡΡΠΈΡΠ»ΡΠ΅ΠΌ Π³ΡΠ°Π½ΠΈΡΡ
int16_t minX = 32767, maxX = -32768;
int16_t minY = 32767, maxY = -32768;
int32_t totalMass = 0;
int32_t cx = 0, cy = 0;
for (uint8_t i = 0; i < count; i++) {
Particle* p = &particles[particleList[i]];
if (p->x < minX) minX = p->x;
if (p->x > maxX) maxX = p->x;
if (p->y < minY) minY = p->y;
if (p->y > maxY) maxY = p->y;
totalMass += p->mass;
cx += p->x * p->mass;
cy += p->y * p->mass;
}
node->minX = minX;
node->maxX = maxX;
node->minY = minY;
node->maxY = maxY;
node->mass = totalMass;
node->x = cx / totalMass;
node->y = cy / totalMass;
// ΠΡΠ»ΠΈ Π² ΡΠ·Π»Π΅ ΠΎΠ΄Π½Π° ΡΠ°ΡΡΠΈΡΠ° ΠΈΠ»ΠΈ ΠΌΠ°Π»Π΅Π½ΡΠΊΠΈΠΉ ΡΠ°Π·ΠΌΠ΅Ρ - ΠΎΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌΡΡ
if (count <= 1 || (maxX - minX < 5 && maxY - minY < 5)) {
node->childCount = 0;
return;
}
// Π Π°Π·Π±ΠΈΠ²Π°Π΅ΠΌ Π½Π° 4 ΠΏΠΎΠ΄ΡΠ·Π»Π°
int16_t midX = (minX + maxX) / 2;
int16_t midY = (minY + maxY) / 2;
uint8_t childLists[4][PARTICLE_COUNT];
uint8_t childCounts[4] = {0, 0, 0, 0};
for (uint8_t i = 0; i < count; i++) {
Particle* p = &particles[particleList[i]];
uint8_t quadrant = 0;
if (p->x > midX) quadrant |= 1;
if (p->y > midY) quadrant |= 2;
childLists[quadrant][childCounts[quadrant]++] = particleList[i];
}
// Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ Π΄ΠΎΡΠ΅ΡΠ½ΠΈΠ΅ ΡΠ·Π»Ρ
for (uint8_t i = 0; i < 4; i++) {
if (childCounts[i] > 0) {
node->children[i] = &nodes[nodeCount++];
buildQuadTree(nodeCount - 1, childLists[i], childCounts[i]);
node->childCount++;
} else {
node->children[i] = NULL;
}
}
}
// ΠΡΡΠΈΡΠ»Π΅Π½ΠΈΠ΅ ΡΠΈΠ»Ρ (Π°Π»Π³ΠΎΡΠΈΡΠΌ ΠΠ°ΡΠ½ΡΠ°-Π₯Π°Ρ)
void computeGravity(uint8_t particleIdx, uint8_t nodeIdx) {
Particle* p = &particles[particleIdx];
QuadNode* node = &nodes[nodeIdx];
// ΠΡΠ»ΠΈ ΡΡΠΎ Π»ΠΈΡΡ ΠΈΠ»ΠΈ ΡΠ°ΡΡΠΈΡΠ° Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ Π΄Π°Π»Π΅ΠΊΠΎ
int16_t dx = node->x - p->x;
int16_t dy = node->y - p->y;
int16_t dist2 = dx * dx + dy * dy + SOFTENING;
if (node->childCount == 0 ||
(node->maxX - node->minX) * (node->maxX - node->minX) < dist2) {
// ΠΡΡΠΈΡΠ»ΡΠ΅ΠΌ ΡΠΈΠ»Ρ ΠΊΠ°ΠΊ ΠΎΡ ΠΎΠ΄Π½ΠΎΠΉ Π±ΠΎΠ»ΡΡΠΎΠΉ ΠΌΠ°ΡΡΡ
int16_t invDist = 1 / sqrt(dist2);
int16_t force = node->mass * invDist;
p->vx += dx * force / 1000;
p->vy += dy * force / 1000;
} else {
// Π Π΅ΠΊΡΡΡΠΈΠ²Π½ΠΎ ΠΏΡΠΎΡ
ΠΎΠ΄ΠΈΠΌ ΠΏΠΎ Π΄Π΅ΡΡΠΌ
for (uint8_t i = 0; i < 4; i++) {
if (node->children[i] != NULL) {
computeGravity(particleIdx, node->children[i] - nodes);
}
}
}
}
// ΠΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ Π³Π°Π»Π°ΠΊΡΠΈΠΊΠΈ
void initGalaxy() {
for (uint8_t i = 0; i < PARTICLE_COUNT; i++) {
// Π‘ΠΏΠΈΡΠ°Π»ΡΠ½Π°Ρ Π³Π°Π»Π°ΠΊΡΠΈΠΊΠ°
float angle = random(0, 360) * 3.14159 / 180;
float radius = random(0, 100) * random(0, 100) / 100;
particles[i].x = radius * cos(angle);
particles[i].y = radius * sin(angle);
particles[i].vx = -radius * sin(angle) * 0.1;
particles[i].vy = radius * cos(angle) * 0.1;
particles[i].mass = random(10, 100);
}
}
// Π‘ΠΈΠΌΡΠ»ΡΡΠΈΡ Π³Π°Π»Π°ΠΊΡΠΈΠΊΠΈ
void simulateGalaxy() {
// Π‘ΡΡΠΎΠΈΠΌ ΠΊΠ²Π°Π΄ΡΠΎΠ΄Π΅ΡΠ΅Π²ΠΎ
nodeCount = 0;
uint8_t allParticles[PARTICLE_COUNT];
for (uint8_t i = 0; i < PARTICLE_COUNT; i++) {
allParticles[i] = i;
}
buildQuadTree(nodeCount++, allParticles, PARTICLE_COUNT);
// ΠΡΡΠΈΡΠ»ΡΠ΅ΠΌ ΡΠΈΠ»Ρ
for (uint8_t i = 0; i < PARTICLE_COUNT; i++) {
computeGravity(i, 0);
}
// ΠΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ ΠΏΠΎΠ·ΠΈΡΠΈΠΈ
for (uint8_t i = 0; i < PARTICLE_COUNT; i++) {
particles[i].x += particles[i].vx * TIME_STEP / 1000;
particles[i].y += particles[i].vy * TIME_STEP / 1000;
}
}
// ΠΡΡΠΈΡΠΎΠ²ΠΊΠ° Π³Π°Π»Π°ΠΊΡΠΈΠΊΠΈ Π½Π° OLED
void renderGalaxy() {
// ΠΡΠΈΡΠ°Π΅ΠΌ ΡΠΊΡΠ°Π½
clearScreen();
for (uint8_t i = 0; i < PARTICLE_COUNT; i++) {
uint8_t screenX = 64 + particles[i].x / 4;
uint8_t screenY = 32 + particles[i].y / 4;
if (screenX < 128 && screenY < 64) {
drawPixel(screenX, screenY, 1);
}
}
updateDisplay();
}
void setup() {
initGalaxy();
initOLED();
}
void loop() {
for (uint8_t i = 0; i < 10; i++) {
simulateGalaxy();
}
renderGalaxy();
delay(50);
}