太阳系运动模型
太阳系运动模型
基本思路
首先,为了让行星运动看起来更加地逼真,采用斜二测画法计算行星的轨道,并将其绘制出来,再将无背景的PNG行星图片按照一定的大小比例绘制在对应的轨道上的某一点。同时,为了让行星运动起来,我们需要用一个循环,不停地计算行星的坐标,并将其绘制。在这之前,要将之前绘制的所有图片进行擦除,也就是清屏(这一操作放在循环当中)。在执行循环的过程中要在绘制图片和清屏之前加上一个时间间隔,这样才能让我们看到图片。这样呈现出的效果就是每个行星在其对应的轨道上运动。这个过程采用一个线程来执行。
此程序的核心算法
计算轨道的方程和计算行星的坐标在原理上是一致的,绘制轨道就是把行星在轨道上的每一点的坐标计算出来,并画上一个点,为了让效果更加明显,我采用了绘制一个半径为2个像素点的圆形的办法。
接下来我们考虑如何来计算轨道上每个点的坐标。因为考虑到行星运动近似圆周运动,因此我们采用将极坐标转化为直角坐标系的办法,即确定了轨道半径和角度,便可以计算出该坐标了。
数学公式如下:
y=√2/4 × r × sin(θ)
x=r × cos(θ)+y
在程序中定义的角度angle是一般的角度,要将它转换成弧度制,于是前面的θ应该写成(angle/180)*π。具体的实现方法如下:
/**
*
* @param r 半径
* @param angle 角度
* @return 行星相对运动中心的Y坐标
*/
public int setY(double r,double angle) {
//由于屏幕中的原点是在左上角,因此y坐标添加一个负号
return -(int)(r*Math.sin((angle/180)*Math.PI)*Math.sqrt(2)/4);
}
public int setX(double r,double angle,int y) {
return (int)(r*Math.cos((angle/180)*Math.PI))-y;
}
计算出的这些坐标需要加上屏幕中心点的坐标(让轨道移动到屏幕中间)。
遇到的问题和解决办法
在绘制行星的过程中要考虑哪个行星先画,原则上y坐标较小的先画(在屏幕上y坐标更小意味着它距离观测点更远)。这样,当行星图片重叠时,相对观测点更近的图片后画便可以覆盖更远的那张图片,可以呈现更加真实的效果。当考虑多个行星的重叠问题时,它的情况呈指数增长,显得极其困难,由于能力有限,我想不到其他更加简便的方法(欢迎各位大神提出自己的想法),只能对处于*的太阳、水星、金星做了相应的处理,对于其他的行星通过控制他们间的距离避免图像重叠问题。但这种方法使得离太阳的距离比例不够协调(虽然本来比例就无法真实还原,因为太阳相对地球等行星,它的半径过于大,并且木星与太阳的距离是地球与太阳的距离的29倍之多)。
除了行星间的图片重叠问题,还有轨道绘制问题,一般情况下,轨道最先画,确保轨道不会挡住行星,但考虑到*的太阳会将水星与金星的一部分轨道覆盖,所以这部分的轨道绘制也要做特殊处理。
还有一个小问题,轨道是具有宽度的,要让行星的中心位于轨道的中心就要做相应的调整。具体实现如下:
for (int i = 0; i < 2700; i++) { //中间会发生重叠的轨道分为两部分分开画
int y1 = setY(r[1], line_angle-20);
int x1 = setX(r[1], line_angle-20, y1);
g.fillOval(x1 + CENTRAL_X - 2, y1 + CENTRAL_Y - 2, 4, 4);
int y2 = setY(r[2], line_angle-20);
int x2 = setX(r[2], line_angle-20, y2);
g.fillOval(x2 + CENTRAL_X - 2, y2 + CENTRAL_Y - 2, 4, 4);
line_angle += 0.1;
}
if (y[1] > 0) {// 水星在太阳前
if (y[2] > 0) {// 金星在太阳前
g.drawImage(Sun.getImage(), CENTRAL_X - size[0] / 2, CENTRAL_Y - size[0] / 2, size[0], size[0],
null);
for (int i = 0; i < 900; i++) {//另一半轨道
int y1 = setY(r[1], line_angle-20);
int x1 = setX(r[1], line_angle-20, y1);
g.fillOval(x1 + CENTRAL_X - 2, y1 + CENTRAL_Y - 2, 4, 4);
int y2 = setY(r[2], line_angle-20);
int x2 = setX(r[2], line_angle-20, y2);
g.fillOval(x2 + CENTRAL_X - 2, y2 + CENTRAL_Y - 2, 4, 4);
line_angle += 0.1;
}
g.drawImage(Mercury.getImage(), x[1] + CENTRAL_X - size[1] / 2, y[1] + CENTRAL_Y - size[1] / 2,
size[1], size[1], null);
g.drawImage(Venus.getImage(), x[2] + CENTRAL_X - size[2] / 2, y[2] + CENTRAL_Y - size[2] / 2,
size[2], size[2], null);
} else {// 金星在太阳后
g.drawImage(Venus.getImage(), x[2] + CENTRAL_X - size[2] / 2, y[2] + CENTRAL_Y - size[2] / 2,
size[2], size[2], null);
g.drawImage(Sun.getImage(), CENTRAL_X - size[0] / 2, CENTRAL_Y - size[0] / 2, size[0], size[0],
null);
for (int i = 0; i < 900; i++) {//另一半轨道
int y1 = setY(r[1], line_angle-20);
int x1 = setX(r[1], line_angle-20, y1);
g.fillOval(x1 + CENTRAL_X - 2, y1 + CENTRAL_Y - 2, 4, 4);
int y2 = setY(r[2], line_angle-20);
int x2 = setX(r[2], line_angle-20, y2);
g.fillOval(x2 + CENTRAL_X - 2, y2 + CENTRAL_Y - 2, 4, 4);
line_angle += 0.1;
}
g.drawImage(Mercury.getImage(), x[1] + CENTRAL_X - size[1] / 2, y[1] + CENTRAL_Y - size[1] / 2,
size[1], size[1], null);
}
} else {//水星在太阳后
if (y[2] > 0) {//金星在太阳前
g.drawImage(Mercury.getImage(), x[1] + CENTRAL_X - size[1] / 2, y[1] + CENTRAL_Y - size[1] / 2,
size[1], size[1], null);
g.drawImage(Sun.getImage(), CENTRAL_X - size[0] / 2, CENTRAL_Y - size[0] / 2, size[0], size[0],
null);
for (int i = 0; i < 900; i++) {//另一半轨道
int y1 = setY(r[1], line_angle-20);
int x1 = setX(r[1], line_angle-20, y1);
g.fillOval(x1 + CENTRAL_X - 2, y1 + CENTRAL_Y - 2, 4, 4);
int y2 = setY(r[2], line_angle-20);
int x2 = setX(r[2], line_angle-20, y2);
g.fillOval(x2 + CENTRAL_X - 2, y2 + CENTRAL_Y - 2, 4, 4);
line_angle += 0.1;
}
g.drawImage(Venus.getImage(), x[2] + CENTRAL_X - size[2] / 2, y[2] + CENTRAL_Y - size[2] / 2,
size[2], size[2], null);
} else {//金星在太阳后
g.drawImage(Venus.getImage(), x[2] + CENTRAL_X - size[2] / 2, y[2] + CENTRAL_Y - size[2] / 2,
size[2], size[2], null);
g.drawImage(Mercury.getImage(), x[1] + CENTRAL_X - size[1] / 2, y[1] + CENTRAL_Y - size[1] / 2,
size[1], size[1], null);
g.drawImage(Sun.getImage(), CENTRAL_X - size[0] / 2, CENTRAL_Y - size[0] / 2, size[0], size[0],
null);
for (int i = 0; i < 900; i++) {//另一半轨道
int y1 = setY(r[1], line_angle-20);
int x1 = setX(r[1], line_angle-20, y1);
g.fillOval(x1 + CENTRAL_X - 2, y1 + CENTRAL_Y - 2, 4, 4);
int y2 = setY(r[2], line_angle-20);
int x2 = setX(r[2], line_angle-20, y2);
g.fillOval(x2 + CENTRAL_X - 2, y2 + CENTRAL_Y - 2, 4, 4);
line_angle += 0.1;
}
}
}
在一开始做此处的轨道处理时我考虑的不够周到,造成了一个小bug,上面的代码是修改后的。以下是有bug的代码(仅供参考):
for (int i = 0; i < 1800; i++) {
int y1 = setY(r[1], line_angle);
int x1 = setX(r[1], line_angle, y1);
g.fillOval(x1 + CENTRAL_X - 2, y1 + CENTRAL_Y - 2, 4, 4);
int y2 = setY(r[2], line_angle);
int x2 = setX(r[2], line_angle, y2);
g.fillOval(x2 + CENTRAL_X - 2, y2 + CENTRAL_Y - 2, 4, 4);
line_angle += 0.1;
}
此代码执行两次,先绘制一半的轨道,在绘制太阳后,绘制另一半(太阳前方的轨道)。
出现的bug如下:
具体的出现此bug的原因有兴趣的读者可以自己去查看我上方的代码,在此不做赘述了。
完整代码和运行效果
由于屏幕大小有限,天王星和海王星没有绘制。
package com.Solar201028;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JWindow;
public class SunSystem extends JFrame{
public static void main(String[] agrs) {
SunSystem sun=new SunSystem();
sun.mysun();
}
public void mysun() {
this.setTitle("太阳系运动模型");
this.setSize(1900,1020);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(3);
JPanel mainjp=new JPanel();
this.add(mainjp);//主面板
Listener move=new Listener();
mainjp.addMouseListener(move);
this.setVisible(true);
Graphics g=mainjp.getGraphics();
move.setG(g);
}
}
package com.Solar201028;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
public class Listener implements MouseListener{
private int begin=0;
private Planet planet=new Planet();
public void setG(Graphics g) {
planet.setG(g);
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
if(begin==0) {
begin++;
planet.start();
}
planet.turn();//控制开始与暂停
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
package com.Solar201028;
import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Random;
import javax.swing.ImageIcon;
public class Planet extends Thread{
private static final int CENTRAL_X=942;//中心X坐标
private static final int CENTRAL_Y=490;//中心Y坐标
private static final int LINE_SIZE=4;//轨道宽度
//依次为太阳,水星,金星,地球,月球,火星,木星,土星,天王星,海王星的尺寸
private int[] size= {200,15,30,36,12,25,80,100,70,70};
//依次为,水星,金星,地球,月球,火星,木星,土星,天王星,海王星的轨道半径,第一位0空出,使数组与size对应
private double[] r= {0,90,160,300,60,450,600,820,900,1000};
private int[] x=new int[9];//行星的X坐标
private int[] y=new int[9];//行星的Y坐标
private double[] angle=new double[9]; //绘制行星所需角度
private double line_angle=0;//绘制轨道时所需的角度
private URI uri;
private URL url;
private AudioClip ac;
private boolean turn=false;
private Graphics G; //面板画布
public void setG(Graphics g) {
this.G=g;
}
public void turn() {
this.turn=!this.turn;
}
/**
*
* @param r 半径
* @param angle 角度
* @return 行星相对运动中心的Y坐标
*/
public int setY(double r,double angle) {
return -(int)(r*Math.sin((angle/180)*Math.PI)*Math.sqrt(2)/4);
}
public int setX(double r,double angle,int y) {
return (int)(r*Math.cos((angle/180)*Math.PI))-y;
}
public void run() {
for(int i =0;i<angle.length;i++) {
angle[i]=new Random().nextInt(360); //让行星从轨道上随机的一个位置出发
}
//创建缓冲图片
BufferedImage buff=new BufferedImage(1900,1000,BufferedImage.TYPE_INT_ARGB);
Graphics g=buff.getGraphics();
ImageIcon background=new ImageIcon("image/cosmos.jpg");
ImageIcon Sun =new ImageIcon("image/太阳.png");
ImageIcon Mercury =new ImageIcon("image/水星.png");
ImageIcon Venus =new ImageIcon("image/金星.png");
ImageIcon Earth =new ImageIcon("image/地球.png");
ImageIcon Moon = new ImageIcon("image/月球.png");
ImageIcon Mars = new ImageIcon("image/火星.png");
ImageIcon Jupiter = new ImageIcon("image/木星.png");
ImageIcon Saturn = new ImageIcon("image/土星.png");
// ImageIcon Uranus = new ImageIcon("image/天王星.png");
// ImageIcon Neptune = new ImageIcon("image/海王星.png");
//添加背景音乐
File f1 = new File("music/Cornfield Chase.wav");
try {
uri = f1.toURI();
url = uri.toURL();
} catch (MalformedURLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
ac = Applet.newAudioClip(url);
ac.loop();
while (true) {
try {
Thread.sleep(42);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (turn) {
g.drawImage(background.getImage(), 0, 0, buff.getWidth(), buff.getHeight(), null);
for (int i = 1; i < 8; i++) {
y[i] = setY(r[i], angle[i]);
x[i] = setX(r[i], angle[i], y[i]);
}
g.setColor(Color.BLACK);
for (int i = 0; i < 3600; i++) {//轨道
for (int j = 3; j < 8; j++) {
if (j == 4) {
int y0 = setY(r[j], line_angle);
int x0 = setX(r[j], line_angle, y0);
g.fillOval(x0 + x[3] + CENTRAL_X - 2, y0 + y[3] + CENTRAL_Y - 2, 4, 4); //月球轨道
} else {
int y0 = setY(r[j], line_angle);
int x0 = setX(r[j], line_angle, y0);
g.fillOval(x0 + CENTRAL_X - 2, y0 + CENTRAL_Y - 2, 4, 4);
}
}
line_angle += 0.1;
}
for (int i = 0; i < 2700; i++) { //中间会发生重叠的轨道分为两部分分开画
int y1 = setY(r[1], line_angle-20);
int x1 = setX(r[1], line_angle-20, y1);
g.fillOval(x1 + CENTRAL_X - 2, y1 + CENTRAL_Y - 2, 4, 4);
int y2 = setY(r[2], line_angle-20);
int x2 = setX(r[2], line_angle-20, y2);
g.fillOval(x2 + CENTRAL_X - 2, y2 + CENTRAL_Y - 2, 4, 4);
line_angle += 0.1;
}
if (y[1] > 0) {// 水星在太阳前
if (y[2] > 0) {// 金星在太阳前
g.drawImage(Sun.getImage(), CENTRAL_X - size[0] / 2, CENTRAL_Y - size[0] / 2, size[0], size[0],
null);
for (int i = 0; i < 900; i++) {
int y1 = setY(r[1], line_angle-20);
int x1 = setX(r[1], line_angle-20, y1);
g.fillOval(x1 + CENTRAL_X - 2, y1 + CENTRAL_Y - 2, 4, 4);
int y2 = setY(r[2], line_angle-20);
int x2 = setX(r[2], line_angle-20, y2);
g.fillOval(x2 + CENTRAL_X - 2, y2 + CENTRAL_Y - 2, 4, 4);
line_angle += 0.1;
}
g.drawImage(Mercury.getImage(), x[1] + CENTRAL_X - size[1] / 2, y[1] + CENTRAL_Y - size[1] / 2,
size[1], size[1], null);
g.drawImage(Venus.getImage(), x[2] + CENTRAL_X - size[2] / 2, y[2] + CENTRAL_Y - size[2] / 2,
size[2], size[2], null);
} else {// 金星在太阳后
g.drawImage(Venus.getImage(), x[2] + CENTRAL_X - size[2] / 2, y[2] + CENTRAL_Y - size[2] / 2,
size[2], size[2], null);
g.drawImage(Sun.getImage(), CENTRAL_X - size[0] / 2, CENTRAL_Y - size[0] / 2, size[0], size[0],
null);
for (int i = 0; i < 900; i++) {
int y1 = setY(r[1], line_angle-20);
int x1 = setX(r[1], line_angle-20, y1);
g.fillOval(x1 + CENTRAL_X - 2, y1 + CENTRAL_Y - 2, 4, 4);
int y2 = setY(r[2], line_angle-20);
int x2 = setX(r[2], line_angle-20, y2);
g.fillOval(x2 + CENTRAL_X - 2, y2 + CENTRAL_Y - 2, 4, 4);
line_angle += 0.1;
}
g.drawImage(Mercury.getImage(), x[1] + CENTRAL_X - size[1] / 2, y[1] + CENTRAL_Y - size[1] / 2,
size[1], size[1], null);
}
} else {//水星在太阳后
if (y[2] > 0) {//金星在太阳前
g.drawImage(Mercury.getImage(), x[1] + CENTRAL_X - size[1] / 2, y[1] + CENTRAL_Y - size[1] / 2,
size[1], size[1], null);
g.drawImage(Sun.getImage(), CENTRAL_X - size[0] / 2, CENTRAL_Y - size[0] / 2, size[0], size[0],
null);
for (int i = 0; i < 900; i++) {
int y1 = setY(r[1], line_angle-20);
int x1 = setX(r[1], line_angle-20, y1);
g.fillOval(x1 + CENTRAL_X - 2, y1 + CENTRAL_Y - 2, 4, 4);
int y2 = setY(r[2], line_angle-20);
int x2 = setX(r[2], line_angle-20, y2);
g.fillOval(x2 + CENTRAL_X - 2, y2 + CENTRAL_Y - 2, 4, 4);
line_angle += 0.1;
}
g.drawImage(Venus.getImage(), x[2] + CENTRAL_X - size[2] / 2, y[2] + CENTRAL_Y - size[2] / 2,
size[2], size[2], null);
} else {//金星在太阳后
g.drawImage(Venus.getImage(), x[2] + CENTRAL_X - size[2] / 2, y[2] + CENTRAL_Y - size[2] / 2,
size[2], size[2], null);
g.drawImage(Mercury.getImage(), x[1] + CENTRAL_X - size[1] / 2, y[1] + CENTRAL_Y - size[1] / 2,
size[1], size[1], null);
g.drawImage(Sun.getImage(), CENTRAL_X - size[0] / 2, CENTRAL_Y - size[0] / 2, size[0], size[0],
null);
for (int i = 0; i < 900; i++) {
int y1 = setY(r[1], line_angle-20);
int x1 = setX(r[1], line_angle-20, y1);
g.fillOval(x1 + CENTRAL_X - 2, y1 + CENTRAL_Y - 2, 4, 4);
int y2 = setY(r[2], line_angle-20);
int x2 = setX(r[2], line_angle-20, y2);
g.fillOval(x2 + CENTRAL_X - 2, y2 + CENTRAL_Y - 2, 4, 4);
line_angle += 0.1;
}
}
}
g.drawImage(Earth.getImage(), x[3] + CENTRAL_X - size[3] / 2, y[3] + CENTRAL_Y - size[3] / 2, size[3],
size[3], null);
g.drawImage(Moon.getImage(), x[4] + x[3] + CENTRAL_X - size[4] / 2,
y[4] + y[3] + CENTRAL_Y - size[4] / 2, size[4], size[4], null);
g.drawImage(Mars.getImage(), x[5] + CENTRAL_X - size[5] / 2, y[5] + CENTRAL_Y - size[5] / 2, size[5],
size[5], null);
g.drawImage(Jupiter.getImage(), x[6] + CENTRAL_X - size[6] / 2, y[6] + CENTRAL_Y - size[6] / 2, size[6],
size[6], null);
g.drawImage(Saturn.getImage(), x[7] + CENTRAL_X - size[7] / 2, y[7] + CENTRAL_Y - size[7] / 2, size[7],
size[7], null);
// 将缓冲图片画到面板上
G.drawImage(buff, 0, 0, null);
// 运动速度
angle[1] += 4;
angle[2] += 1.5;
angle[3] += 1;
angle[4] += 12;
angle[5] += 0.5;
angle[6] += 0.1;
angle[7] += 0.06;
}
}
}
}
由于视频无法上传,只能展示图片了。
背景音乐和图片就不做提供了,有兴趣的读者可以自行寻找图片和背景音乐。
最后,还存在一个小问题,由于绘制的坐标强制转换成int型,丢失了精度,导致行星运动轨迹不够平滑,如果大家有什么办法,欢迎大家提出自己的想法!
下一篇: 【OpenGL】模拟太阳系
推荐阅读
-
关于PHP中数组模型的一点猜想(by misko lee)_PHP教程
-
tensorflow模型继续训练 fineturn实例
-
WX微信和支付宝ZFB运动_Python脚本(思路+源码)
-
maya晶格变形器怎么调整模型点多的模型?
-
Redis:基于Redis的秒杀方案,缓存秒杀模型,先到先得、随机拼运气式秒杀算法Java实现,秒杀限流算法,漏桶算法、令牌桶算法伪代码
-
3Dmax2010怎么设计灯壳模型?
-
Maya2015怎么制作招财猫? Maya中NEX模型编辑功能的使用方法
-
请教MVC中的控制器到底是做什么用啊 跟模型有什么区别
-
Laravel 5框架学习之模型、控制器、视图基础流程
-
PowerDesigner的概念数据模型CDM