Star Simulation

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