#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;
}