这段时间都在寝室里自学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));}//绘制所有行星@Overridepublic 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);}@Overridepublic 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会遇到很多问题,如有问题还请多多指正。