【创意编程】《无限》:宇宙、三体、星云和蚕丝
————————————————————————————————
————————————————————————————————
一颗恒星。不知道有多少不起眼的小行星日日夜夜绕着它,有些却飞向无限的远点。宇宙有限吗?它们还会回来吗?
一个没有调好的三体模型
不知道什么给我的灵感,也不知道从哪来的想法,想着既然要做一个力,不如做大点,做个大的,甚至做个完整的系统。一开始其实没有很多想法,只想做一个三体的运动,但是本来三体就不稳定,而且我调了一下午的参数也没有调出一个相对稳定的且包含几种基本的天体状态的参数来。而且简单的三体运动在实现技术上也其实没有什么很大的困难,而且三个球孤零零的,也不是很好看。于是就想不如先做一个恒星系,然后去描绘他们的运动,或许可以有一些有意思的图案出现。于是就去做了,而且意外地发现效果真的还不错,是我想要的那种感觉。
由于我想要模拟看起来很舒服的天体系统,所以天体的运动都不会很快,在这里放全过程的gif是不现实的,就只能截图示意一下效果。
我们将在宇宙中随机分布一些具有随机初速度、随机位置、随机质量(小质量)的小行星。考虑到计算性能和,并且我们还需要画轨迹,我们的小行星数目不应该太多。上面的动图由于在台式机上跑性能较好,给了50个行星。如果是笔记本上,20个就已经够了。
恒星的质量和行星不在一个数量级。在这个系统里,我为了显示的效果,定义了恒星的质量为1000,行星则为1到3。
所有的星球被加入后构成星球列表。对于画布上的每一个星球,我们都需要计算其他所有星球对它施加的万有引力。在这个系统中,恒星只有一颗且远远重于行星,周围其他行星的万有引力就显得微乎其微。
受到万有引力作用,行星开始改变运动轨迹以及速度。
一些初速度大的直接逃逸了。虽然它终究还是会被拉回来,但是这已经不重要了。
另一些的运动轨迹则非常直观地刻画了高中物理课上的天体物理模型。
在这之后,轨迹变得复杂,画面中的轨迹线也变多。
这个图不好看。我们重新运行一下看看。
最终构成了非常美妙的样子。
运动轨迹真的好妙呀。
————————————————
万有引力仿真
根据万有引力公式,F = G * (m1 * m2)/ r^2。
我们就根据这个公式把力写出来。
与前面的作业不同,这里的速度因为涉及到旋转、椭圆计算,我们需要的是PVector类型的速度以及加速度,力也是有方向的。这个力是所有星球给予这个星球的力的矢量叠加,故也需要用PVector类型来表示。
public void calculateForce(ArrayList<Planet> planets, int currentIndex){
for (int i = 0; i< planets.size(); i++){
if (i != currentIndex){
Planet p = planets.get(i);
PVector offset = new PVector((p.position.x - position.x), (p.position.y - position.y));
float distance = sqrt(sq(offset.x) + sq(offset.y));
float thisforce = mass * p.mass / sq(distance);//no direction
PVector forceNorm = offset.normalize();
PVector thisForce = forceNorm.mult(thisforce);
force = PVector.add(force, thisForce);
}
}
}
其中,我们需要施加每一个来自自己以外所有星球的引力,就需要在调用方法的时候传入所有星球的数组,并对自己以外的任何星球进行同等的处理,才能够得到准确的合力。当然,在我们这个认为恒星是中心的系统中,恒星不需要进行这样的处理。
应用万有引力
public void applyForce(){
acceleration = force.div(mass);
velocity.add(acceleration);
position.add(velocity);
acceleration = new PVector(0, 0);
force = new PVector(0,0);
}
————————————
绘制轨迹
和上一章使用的方法一样,在上面蒙一层透明画布,不擦除轨迹,用于记录并且显示轨迹。
PGraphics route;
route = createGraphics(width, height);
void draw(){
//background(0);
image(route, 0, 0);
fill(255,0,0);
noStroke();
ellipse(solar.position.x, solar.position.y, 20,20);
for (int i = 1; i < planets.size(); i++){
Planet p = planets.get(i);
p.calculateForce(planets, i);
p.applyForce();
p.display();
route.beginDraw();
route.strokeWeight(0.1);
route.stroke(255);
route.point(p.position.x, p.position.y);
route.endDraw();
}
}
我很喜欢这个代码,也很喜欢这个想法。或许我还可以把它做成一个完整的可以人手动创造的系统。
——————————————————————————
完整代码
主类:
ArrayList<Planet> planets;
PGraphics route;
Planet solar;
int planetNum = 50;
void setup(){
size(1600,1000);
background(0);
route = createGraphics(width, height);
planets = new ArrayList<Planet>();
solar = new Planet(1000, width/2,height/2, 0, 0);
planets.add(solar);
noStroke();
//fill(255,0,255);
//m, x, y;
for (int i = 0; i< planetNum; i++){
Planet p = new Planet(random(1,3), random(width), random(height), random(-2, 2), random(-2, 2));
planets.add(p);
}
}
void draw(){
//background(0);
image(route, 0, 0);
fill(255,0,0);
noStroke();
ellipse(solar.position.x, solar.position.y, 20,20);
for (int i = 1; i < planets.size(); i++){
Planet p = planets.get(i);
p.calculateForce(planets, i);
p.applyForce();
p.display();
route.beginDraw();
route.strokeWeight(0.1);
route.stroke(255);
route.point(p.position.x, p.position.y);
route.endDraw();
}
}
行星的Planet类:
class Planet{
float mass;
PVector position;
PVector velocity;
PVector acceleration;
PVector force;
Planet(float m, float x, float y, float vx, float vy){
mass = m;
position = new PVector(x, y);
velocity = new PVector(vx, vy);
acceleration = new PVector(0, 0);
force = new PVector(0, 0);
}
public void calculateForce(ArrayList<Planet> planets, int currentIndex){
for (int i = 0; i< planets.size(); i++){
if (i != currentIndex){
Planet p = planets.get(i);
PVector offset = new PVector((p.position.x - position.x), (p.position.y - position.y));
float distance = sqrt(sq(offset.x) + sq(offset.y));
float thisforce = mass * p.mass / sq(distance);//no direction
PVector forceNorm = offset.normalize();
PVector thisForce = forceNorm.mult(thisforce);
force = PVector.add(force, thisForce);
}
}
}
public void applyForce(){
acceleration = force.div(mass);
velocity.add(acceleration);
position.add(velocity);
acceleration = new PVector(0, 0);
force = new PVector(0,0);
}
public void display(){
fill(255, 255, 255);
ellipse(position.x, position.y, 1, 1);
stroke(255);
strokeWeight(1);
}
}
三体
在我已经写好的系统中,对恒星、行星进行了同样的处理,所以要想模拟单体模型,只需要加进去两个恒星,把行星舍去。
三体本身就是一个非常不稳定的状态,我调了一天的参数还是没能找到相对稳定的状态。但它真的不难。
不多赘述了。