小言_互联网的博客

用python做一个漂亮的太阳系运动模拟

477人阅读  评论(0)

贴一张静态图

GIF不能支持细腻的颜色,为了看到动态效果,请看视频。

太阳系的一个小知识

资源素材

太阳系现在只有8大行星,连太阳一起,一共是9张图片。如果没有的朋友,可以到文末的下载地址下载。


  
  1. def openSolor(solar):
  2. def loadImg(name):
  3. str1= os.path.join(basePath, name+ '.png')
  4. img= Image.open(str1)
  5. solar[name]= img
  6. basePath= r'D:\太阳系\素材'
  7. #
  8. loadImg( 'sun')
  9. loadImg( 'venus')
  10. loadImg( 'jupiter')
  11. loadImg( 'earth')
  12. loadImg( 'mars')
  13. loadImg( 'mercury')
  14. loadImg( 'neptune')
  15. loadImg( 'pluto')
  16. loadImg( 'uranus')
  17. loadImg( 'saturn')

基本运动原理

每颗行星的运动轨迹都是椭圆的,我们这里用一个参数方程来计算坐标:

  • x=cos(arc)*a
  • y=sin(arc)*b

其中,a,b 是椭圆的长轴和短轴,arc是运行角度,x,y是水平面坐标。

参数的设置

为了效果好看,实际参数不可能是真实的。但有几个关键条件至少应该满足。首先行星顺序别弄错,行星轨道之间的间距不是等距的,而是渐增的。其次是火星和木星直接有一个小行星带,所以这两个行星的轨道之间最好留出一个空隙。还有就是越往外圈的行星,绕行速度越慢。


  
  1. def initSolar(posList):
  2. def getNumber():
  3. return random.randint( 0, 35)* 10
  4. posList[ 'sun']={ 'pos': ( 0, 360), 'rate': 2, 'scale': 1, 'radx': 1, 'layer': 360}
  5. posList[ 'mercury']={ 'rate': 0.15, 'radx': 500, 'arc': getNumber(), 'rady': 200, 'speed': 15}
  6. posList[ 'venus']={ 'rate': 0.2, 'radx': 550, 'arc': getNumber(), 'rady': 250, 'speed': 10}
  7. posList[ 'earth']={ 'rate': 0.2, 'radx': 630, 'arc': getNumber(), 'rady': 320, 'speed': 8}
  8. posList[ 'mars']={ 'rate': 0.2, 'radx': 740, 'arc': getNumber(), 'rady': 410, 'speed': 6}
  9. posList[ 'jupiter']={ 'rate': 0.7, 'radx': 1050, 'arc': getNumber(), 'rady': 650, 'speed': 4}
  10. posList[ 'saturn']={ 'rate': 1, 'radx': 1250, 'arc': getNumber(), 'rady': 800, 'speed': 3}
  11. posList[ 'uranus']={ 'rate': 0.3, 'radx': 1480, 'arc': getNumber(), 'rady': 970, 'speed': 2}
  12. posList[ 'neptune']={ 'rate': 0.3, 'radx': 1740, 'arc': getNumber(), 'rady': 1160, 'speed': 2}

投影

一般的效果是将行星围绕太阳的公转面至于一个水平面上,然后投影到垂直的屏幕上。投影算法不难。


  
  1. x= math.sin(math.radians(a))* radx+ x0
  2. y= math.cos(math.radians(a))* rady+ y0
  3. showX= x
  4. showY= midY- H/(D+y)*y

其中,x,y是公转平面坐标,showX,showY是投影到垂直平面的坐标。H是平面的高度,D是屏幕到太阳系的距离。

从数据来看,我们的太阳系模型是一个非常小的模型,或者电脑屏幕非常大。因为这两者实际差不多大,以至于从观察者的视角就可以出现很明显的近大远小效果。从这种效果就可以知道,数据与真实值差别极为巨大。

近大远小的效果,只与y相关。

        data['scale']= (y0+D)/(y+D)

遮挡效果

为了有真实感,行星之间、行星与轨道之间,轨道与太阳之间等等的遮挡效果是最关键的。

我们的做法是先画后半区,再画太阳,再画前半区。后半区中,远日行星先画;前半区中,近日行星先画。以保证正确的遮挡效果。


  
  1. drawOrb(img, solar, posList, 0, 90, True)
  2. pasteSolor(img, solar, posList)
  3. drawOrb(img, solar, posList, 90, 180, False)

比较复杂一点的是行星与自身轨道之间的遮挡关系。必须实现一线穿一球的效果才好看。而且穿球位置不是固定不变的。这里,我们根据行星所在角度的不同,将轨道拆分为两半来画。一部分轨道是被行星遮挡的,另一部分轨道遮挡行星,但留一些空间,以实现比较自然的穿球效果。


  
  1. drawArc(arc1, arc)
  2. rate= posList[name][ 'rate']* posList[name][ 'scale']
  3. pic= solarImg[name].resize(effect.tupleRound(effect.tupleMul(solarImg[name].size, rate)), Image.ANTIALIAS)
  4. pos= effect.tupleRound(effect.tupleAdd(posList[name][ 'pos'], effect.tupleMul(pic.size, -0.5)))
  5. r, g, b, alpha= pic.split()
  6. img.paste(pic, pos, mask= alpha)
  7. # 穿球点,随arc不同而不同
  8. # 90度位置,在中心穿球,
  9. # 越接近0或180度,越接近球边缘
  10. # 根据这种性质,采用cos来模拟
  11. darc= abs(round(math.cos(math.radians(arc))*solarImg[name].size[ 1]*rate/ 50))
  12. # darc= abs(round(math.cos(math.radians(arc))*5))
  13. # print(name, arc, darc)
  14. drawArc(arc+darc, arc2)

 

素材下载地址:

链接:https://pan.baidu.com/s/18ELL4aL-jHbIbIacMpVbjA 
提取码:5bjj

 


转载:https://blog.csdn.net/xiaorang/article/details/106884311
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场