#include <iostream> #include <cmath> #include <graphics.h> #include <vector> #include <time.h> using namespace std; #define Random(a,b) ((a)+(random(0xFFFFFFFF)%((b)-(a)+(1)))) const int width = 1200, height = 800;//当前窗口宽度和高度 double frameRate = 144.0;//帧率 double frameTime = 1000 / frameRate; double dt = 1 / frameRate;//时间微分 double G = 70;//引力常数(为了适应屏幕显示) int starcount = 100;//最大天体数 INT64 randomSpeed = 100;//天体随机速度区间 INT64 randomMass = 5000;//天体随机质量区间 class PVector//二维向量类 { public: double x; double y; PVector()//构造函数 { x = y = 0; } PVector(double x, double y)//构造函数 { this->x = x; this->y = y; } double mag()//获得向量模的平方 { return x * x + y * y; } double magsqrt()//获得向量模 { return sqrt(this->mag()); } PVector normalize()//获得单位向量 { return PVector(x / magsqrt(), y / magsqrt()); } PVector add(PVector a)//向量加法 { this->x = this->x + a.x; this->y = this->y + a.y; return *this; } PVector sub(PVector a)//向量减法 { this->x = this->x - a.x; this->y = this->y - a.y; return *this; } PVector mult(double n)// 向量乘法 { this->x *= n; this->y *= n; return *this; } PVector div(double n)//向量除法 { this->x /= n; this->y /= n; return *this; } //运算符重载 PVector operator+(const PVector& a) { return PVector(this->x + a.x, this->y + a.y); } PVector operator-() { return PVector(-this->x, -this->y); } PVector operator-(const PVector& a) { return PVector(this->x - a.x, this->y - a.y); } PVector operator*(const double n) { return PVector(n * this->x, n * this->y); } friend PVector operator*(double n, PVector& a) { PVector b; b.x = n * a.x; b.y = n * a.y; return b; } friend std::ostream& operator<<(std::ostream& os, const PVector& a) { os << "(x, y) = (" << a.x << ", " << a.y << ")" << std::endl; return os; } }; class Object { public: vector<PVector>path;//显示路径的数组 int MAXPATH = 100; //路径数组大小 int id; //编号 double mass;//质量 double r; //半径 PVector pos;//位置 PVector vel;//速度 COLORREF color;//颜色 Object() {};//构造函数 ~Object() {};//析构函数 double CalR()//根据质量计算半径 { return pow(this->mass / PI * (3 / 4.0), 1 / 3.0); } Object(double _mass, double _x, double _y, double _vx, double _vy, int _id)//构造函数 { this->mass = _mass; this->pos = PVector(_x, _y); this->vel = PVector(_vx, _vy); this->r = this->CalR(); this->id = _id; this->color = RGB(Random(0, 255), Random(0, 255), Random(0, 255)); } void Show()//画圆和输出id { setcolor(BLACK); setfillcolor(color);//设置填充颜色 fillellipsef(pos.x, pos.y, CalR(),CalR());//画一个无边框填充圆 /*--------------------------------*/ setcolor(WHITE);//设置文字颜色 setfont(15, 0, "monaco");//设置文字大小,样式,字体 TCHAR s[5]; sprintf(s, "%d", id); outtextxy(pos.x - 2, pos.y - 8, s);//输出当前对象id } void Update()//根据速度更新位置,并且消除冗余轨迹 { pos = pos + vel * dt; path.push_back(PVector(pos.x, pos.y)); if (path.size() > (size_t)MAXPATH) path.erase(path.begin()); } void ShowPath()//显示轨迹 { setcolor(color); for (int i = 0 ; i < path.size(); i++) { PVector a = path[i]; PVector b; if (i + 1 == path.size()) b = pos; else { b = path[i + 1]; line(a.x, a.y, b.x, b.y); } } } inline bool operator==(const Object& a)const //逻辑运算符==重载 { return (a.mass == mass) && (pos.x == a.pos.x) && (pos.y == a.pos.y) && (vel.x == a.vel.x) && (vel.y == a.vel.y) && (id == a.id); } inline bool operator!=(const Object& a)const //逻辑运算符!=重载 { return (a.mass == mass) && (pos.x == a.pos.x) && (pos.y == a.pos.y) && (vel.x == a.vel.x) && (vel.y == a.vel.y) && (id == a.id); } }; void CollisionCheck(vector<Object> &bodys)//碰撞检测 { for (size_t cur = 0; cur < bodys.size() && !bodys.empty(); cur++)//当前星球 { Object Origin = bodys[cur]; for (size_t other = 0 , OriginSize = bodys.size(); other < bodys.size() && !bodys.empty(); other++) { if (bodys.size() < OriginSize) { other--; OriginSize--; } if (cur == other) continue;//遇到自己直接跳过 PVector dist = bodys[cur].pos - bodys[other].pos; //方向向量 指向当前 if (dist.magsqrt() <= bodys[cur].CalR() + bodys[other].CalR())//如果两天体之间距离小于两半径之和,则为碰撞 { if (bodys[cur].mass >= bodys[other].mass)//如果当前天体质量 大于 遍历的天体质量 { bodys[cur].vel = (bodys[cur].mass * bodys[cur].vel + bodys[other].mass * bodys[other].vel) * (1 / (bodys[cur].mass + bodys[other].mass));//完全弹性碰撞 动能定理 bodys[cur].mass += bodys[other].mass;//质量相加 bodys.erase(bodys.begin() + other); } else continue; } else//否则互相吸引 { bodys[cur].vel = bodys[cur].vel - dist.normalize() * (G * bodys[other].mass / dist.mag()) * dt; /*bodys[other].vel = bodys[other].vel + dist.normalize() * (G * bodys[cur].mass / dist.mag()) * dt;*/ } } while (Origin != bodys[cur]) { cur--; if(cur<0) cur++; } } } void FindMaxMassAndSetOrigin(vector<Object>& bodys) { Object *max = &bodys[0]; for (size_t i = 0; i < bodys.size() && !bodys.empty(); i++) { if ((*max).mass <= bodys[i].mass) { max = &bodys[i]; } } setviewport(-(*max).pos.x + width / 2, -(*max).pos.y + height / 2,-(*max).pos.x + width * 3 / 2, -(*max).pos.y + height * 3 / 2 , 0 ); } int main() { randomize();//初始化随机种子 vector<Object> bodys;//存放object的容器 for (int i = 0; i < starcount; i++)//依次将随机生成的星球装入容器 { bodys.push_back(Object(Random(0, randomMass), //质量 Random(0, width), //x Random(0, height), //y Random(-randomSpeed, randomSpeed),//vx Random(-randomSpeed, randomSpeed),//vy i)//标号 ); } //bodys.push_back(Object(10000, width / 2 - 100.0, height / 2, 0, -5, 1)); //bodys.push_back(Object(10000, width / 2 + 100.0, height / 2, 0, 5, 2)); setinitmode(INIT_RENDERMANUAL); initgraph(width, height); setbkmode(TRANSPARENT); setrendermode(RENDER_MANUAL); setcaption("Star Simulation"); Sleep(2000); while (1) { for (size_t i = 0; i < bodys.size(); i++)//显示轨迹 { bodys[i].ShowPath(); } for (size_t i = 0; i < bodys.size(); i++)//显示星球 { bodys[i].Show(); bodys[i].Update(); } CollisionCheck(bodys);/*碰撞检测*/ /*-------------------镜头跟踪最大质量天体-------------------*/ Object* max = &bodys[0]; for (size_t i = 0; i < bodys.size() && !bodys.empty(); i++) { if ((*max).mass <= bodys[i].mass) { max = &bodys[i]; } } setviewport(-(*max).pos.x + width / 2, -(*max).pos.y + height / 2, (*max).pos.x + width * 3 / 2, (*max).pos.y + height * 3 / 2, 0 ); /*-----------------------------------------------------------*/ /*-------------------输出FPS和剩余天体数目-------------------*/ setfont(16, 0, "宋体"); setcolor(YELLOW); TCHAR s[100], t[100]; memset(s, 0, 100); memset(t, 0, 100); sprintf(s, "FPS:%.lf", getfps()); sprintf(t, "bodys count:%d", (int)bodys.size()); outtextxy((*max).pos.x - width / 2 + 10, (*max).pos.y - height / 2 + 20, s); outtextxy((*max).pos.x - width / 2 + 10, (*max).pos.y - height / 2 + 40, t); // outtextxy( 10, 20, s); // outtextxy(10, 40, t); /*-----------------------------------------------------------*/ /*-------------------输出存活天体的详细信息-------------------*/ setfont(15, 0, "宋体"); for (size_t i = 0; i < bodys.size() && !bodys.empty(); i++) { setcolor(bodys[i].color); TCHAR a[100]; memset(a, 0, 100); sprintf(a,"[%d] Planet#%d Mass:%.lf Pos(%.lf,%.lf)" , i , bodys[i].id , bodys[i].mass , bodys[i].pos.x , bodys[i].pos.y); outtextxy((*max).pos.x - width / 2 + 10, (*max).pos.y - height / 2 + 70 + 15.0 * i, a); // outtextxy(10,70 + 15.0 * i, a); } /*------------------------------------------------------------*/ delay_fps(frameRate); cleardevice(); } closegraph(); return 0; }