这段时间都在寝室里自学Java,就想自己写个小程序玩一玩。同时,我也是个三体迷,就想着能不能用学的Java来模拟一下三体运动。这个程序算是我正式写模拟三体运动前的一个尝试。
一、程序分析
首先来百度一番查一下太阳、水星、金星和地球的各种参数(非精确):
1.太阳:
质量:kg
2.水星:
质量:kg
轨道半径:m
公转速度:m/s
3.金星
质量:kg
轨道半径:m
公转速度:m/s
4.地球
质量:kg
轨道半径:m
公转速度:m/s
模拟的方式,我采用了万有引力公式和牛顿运动定律的微分形式来模拟。这种方法误差和计算量都比较大,但对这种粗略的模拟,还是可以的。首先用万有引力公式计算出各个行星受到的太阳引力(行星之间的引力就忽略不计了,没必要,当然加上也是可以的),进而计算出每个行星的加速度。用一个变量time来表示时间微元,用每个行星当前的速度vector乘以time就得到行星位移的微元,然后再将行星速度vector更新为vector+行星的加速度乘以时间,再将行星的位置画出来。将这些过程写在一个循环体里,就可宜不断更新行星的位置了。
这里面用到的vector,加速度等都是向量,Java并没有处理向量运算的类,需要自己写一个自定义类Vector2D。每个行星也都定义为自定义类Actor类的子类。绘制行星等在Univers类中。程序入口为Main类。
包结构
- main包:Main类
- vector2D包:Vector2D类
- actor包:Actor类
- univers包:Univers类
二、代码编写
1.Vector2D类
package Vector2D;
import java.awt.Point;
public class Vector2D {
public static Vector2D baseX = new Vector2D(1, 0); //x方向的单位向量
public static Vector2D baseY = new Vector2D(0, 1); //y方向的单位向量
private double x; //向量的x坐标
private double y; //向量的y坐标
private double model; //向量的模
private float theta; //向量的辐角
public Vector2D(double x,double y) {
this.x = x;
this.y = y;
this.model = Math.sqrt(this.x*this.x+this.y*this.y);
this.theta = (float) Math.atan(y/x);
}
public Vector2D(Point p1,Point p2) {
x = p2.getX() - p1.getX();
y = p2.getY() - p1.getY();
model = Math.sqrt(x*x+y*y);
theta = (float) Math.atan(y/x);
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getModel() {
return model;
}
public double getTheta() {
return theta;
}
//向量加法
public Vector2D plus(Vector2D vector2D_2) {
return new Vector2D(x+vector2D_2.getX(),
y+vector2D_2.getY());
}
//向量减法
public Vector2D sub(Vector2D vector2D_2) {
return new Vector2D(x-vector2D_2.getX(),
y-vector2D_2.getY());
}
//求相反向量
public Vector2D inverse() {
return new Vector2D(-x,-y);
}
//数乘
public Vector2D multi(double r) {
return new Vector2D(r*x, r*y);
}
//向量点积
public double dot(Vector2D vector2D) {
return vector2D.x*this.x+vector2D.y*this.y;
}
//获得单位化向量
public Vector2D normal() {
return new Vector2D(x/model,
y/model);
}
//求垂直向量
public Vector2D perp() {
return new Vector2D(-y,x);
}
//向量旋转
public Vector2D roatation(double theta) {
double temp = x*Math.cos(theta)-y*Math.sin(theta);
double newY = x*Math.sin(theta)+y*Math.cos(theta);
return new Vector2D(temp, newY);
}
public String toString() {
return String.format("("+x+","+y+")");
}
}
2.Actor类
package actor;
import java.awt.*;
import Vector2D.Vector2D;
public class Actor {
public double mass; //质量
public Point position; //点位置
public Vector2D acceleration; //加速度
public Vector2D vector; //速度
public Vector2D displacement; //位移
public Vector2D force; //受力
public Actor(double mass,Point position) {
this.mass = mass;
this.position = position;
}
public void drawActor(Graphics g,int height,int width) {
g.fillOval(position.x-width/2, position.y-height/2,height, width);
}
}
3.Univers类
package univers;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.datatransfer.SystemFlavorMap;
import javax.swing.JPanel;
import Vector2D.Vector2D;
import actor.Actor;
public class Univers extends JPanel implements Runnable{
private static double G = 6.67E-11; //万有引力常数
private Point[] star = new Point[500]; //背景的星星
private Point origSun = new Point(319,239); //太阳的位置
private Point origEarth = new Point(469,239); //地球的初始位置
private Point origVenus = new Point(429,239); //金星的初始位置
private Point origMercury = new Point(378,239); //水星的初始位置
private Actor sun = new Actor(2E30,origSun); //太阳
private Actor earth = new Actor(6E24,origEarth); //地球
private Actor venus = new Actor(4.9E24, origVenus); //金星
private Actor mercury = new Actor(3.3E23, origMercury); //水星
private Vector2D earthPosition = new Vector2D(origSun,origEarth); //地球的位置向量
private Vector2D venusPosition = new Vector2D(origSun, origVenus); //金星的位置向量
private Vector2D mercuryPosition = new Vector2D(origSun, origMercury);//水星的位置向量
private Vector2D vectorEarth = new Vector2D(0, 3E4); //地球当前速度
private Vector2D vectorVenus = new Vector2D(0, -3.5E4); //金星当前速度
private Vector2D vectorMercury = new Vector2D(0,4.8E4); //水星当前速度
private long time = 1;
public Univers2() {
for(int i=0;i<500;i++) {
int x=(int) (Math.random()*640);
int y=(int) (Math.random()*480);
star[i] = new Point(x,y);
}
}
private void calculate() {
mercuryCalculate();
venusCalculate();
earthCalculate();
}
//地球轨道的计算
private void earthCalculate() {
Vector2D delta_displacement = vectorEarth.multi(time*1e-9); //乘1e9是对长度缩放
earthPosition = earthPosition.plus(delta_displacement);
double current_acceleration = G*2.0e30/(earthPosition.getModel()*earthPosition.getModel()*1e18);
earth.acceleration = earthPosition.normal().multi(-current_acceleration);
vectorEarth = vectorEarth.plus(earth.acceleration.multi(time));
}
//金星轨道的计算
private void venusCalculate() {
Vector2D delta_displacement = vectorVenus.multi(time*1e-9); //乘1e9是对长度缩放
venusPosition = venusPosition.plus(delta_displacement);
double current_acceleration = G*2.0e30/(venusPosition.getModel()*venusPosition.getModel()*1e18);
venus.acceleration = venusPosition.normal().multi(-current_acceleration);
vectorVenus = vectorVenus.plus(venus.acceleration.multi(time));
}
//水星轨道的计算
private void mercuryCalculate() {
Vector2D delta_displacement = vectorMercury.multi(time*1e-9); //乘1e9是对长度缩放
mercuryPosition = mercuryPosition.plus(delta_displacement);
double current_acceleration = G*2.0e30/(mercuryPosition.getModel()*mercuryPosition.getModel()*1e18);
mercury.acceleration = mercuryPosition.normal().multi(-current_acceleration);
vectorMercury = vectorMercury.plus(mercury.acceleration.multi(time));
}
//绘制所有行星
@Override
public void paint(Graphics g) {
// TODO 自动生成的方法存根
super.paint(g);
for(int i=0;i<500;i++) { //让背景闪烁
g.setColor(Color.white);
if(Math.random()>0.5) {
g.setColor(Color.gray);
}
g.fillOval(star[i].x,star[i].y,2,2);
}
g.setColor(Color.red);
sun.drawActor(g,30,30);
g.setColor(Color.gray);
g.fillOval((int)(origSun.x+mercuryPosition.getX()-1),(int)(origSun.y-mercuryPosition.getY()-1), 6, 6);
g.setColor(Color.yellow);
g.fillOval((int)(origSun.x+venusPosition.getX()-1),(int)(origSun.y-venusPosition.getY()-1), 6,6);
g.setColor(Color.blue);
g.fillOval((int)(origSun.x+earthPosition.getX()-1), (int)(origSun.y-earthPosition.getY()-1),6, 6);
}
@Override
public void run() {
// TODO 自动生成的方法存根
setBackground(Color.black);
while(true) {
repaint();
calculate();
time += 100; //数字可以随便填
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}
4.Main类
package main;
import java.awt.Container;
import javax.swing.JFrame;
import univers.Univers;
import univers.Univers;
public class Main extends JFrame{
private Univers univers = new Univers();
private Container container = getContentPane();
private Thread renderThread = new Thread(univers);
public Main() {
setTitle("行星运动");
setSize(640,480);
setDefaultCloseOperation(EXIT_ON_CLOSE);
container.add(univers);
setVisible(true);
renderThread.start();
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
new Main();
}
}
三、运行结果
灰色的为水星,黄色的为金星,蓝色的为地球,红色是太阳。
下篇文章来写三体运动的模拟。自学Java会遇到很多问题,如有问题还请多多指正。