贴一张静态图
GIF不能支持细腻的颜色,为了看到动态效果,请看视频。
太阳系的一个小知识
资源素材
太阳系现在只有8大行星,连太阳一起,一共是9张图片。如果没有的朋友,可以到文末的下载地址下载。
-
def openSolor(solar):
-
def loadImg(name):
-
str1= os.path.join(basePath, name+
'.png')
-
img= Image.open(str1)
-
solar[name]= img
-
-
basePath=
r'D:\太阳系\素材'
-
#
-
loadImg(
'sun')
-
loadImg(
'venus')
-
loadImg(
'jupiter')
-
loadImg(
'earth')
-
loadImg(
'mars')
-
loadImg(
'mercury')
-
loadImg(
'neptune')
-
loadImg(
'pluto')
-
loadImg(
'uranus')
-
loadImg(
'saturn')
基本运动原理
每颗行星的运动轨迹都是椭圆的,我们这里用一个参数方程来计算坐标:
- x=cos(arc)*a
- y=sin(arc)*b
其中,a,b 是椭圆的长轴和短轴,arc是运行角度,x,y是水平面坐标。
参数的设置
为了效果好看,实际参数不可能是真实的。但有几个关键条件至少应该满足。首先行星顺序别弄错,行星轨道之间的间距不是等距的,而是渐增的。其次是火星和木星直接有一个小行星带,所以这两个行星的轨道之间最好留出一个空隙。还有就是越往外圈的行星,绕行速度越慢。
-
def initSolar(posList):
-
def getNumber():
-
return random.randint(
0,
35)*
10
-
posList[
'sun']={
'pos': (
0,
360),
'rate':
2,
'scale':
1,
'radx':
1,
'layer':
360}
-
posList[
'mercury']={
'rate':
0.15,
'radx':
500,
'arc': getNumber(),
'rady':
200,
'speed':
15}
-
posList[
'venus']={
'rate':
0.2,
'radx':
550,
'arc': getNumber(),
'rady':
250,
'speed':
10}
-
posList[
'earth']={
'rate':
0.2,
'radx':
630,
'arc': getNumber(),
'rady':
320,
'speed':
8}
-
posList[
'mars']={
'rate':
0.2,
'radx':
740,
'arc': getNumber(),
'rady':
410,
'speed':
6}
-
posList[
'jupiter']={
'rate':
0.7,
'radx':
1050,
'arc': getNumber(),
'rady':
650,
'speed':
4}
-
posList[
'saturn']={
'rate':
1,
'radx':
1250,
'arc': getNumber(),
'rady':
800,
'speed':
3}
-
posList[
'uranus']={
'rate':
0.3,
'radx':
1480,
'arc': getNumber(),
'rady':
970,
'speed':
2}
-
posList[
'neptune']={
'rate':
0.3,
'radx':
1740,
'arc': getNumber(),
'rady':
1160,
'speed':
2}
投影
一般的效果是将行星围绕太阳的公转面至于一个水平面上,然后投影到垂直的屏幕上。投影算法不难。
-
x= math.sin(math.radians(a))* radx+ x0
-
y= math.cos(math.radians(a))* rady+ y0
-
showX= x
-
showY= midY- H/(D+y)*y
其中,x,y是公转平面坐标,showX,showY是投影到垂直平面的坐标。H是平面的高度,D是屏幕到太阳系的距离。
从数据来看,我们的太阳系模型是一个非常小的模型,或者电脑屏幕非常大。因为这两者实际差不多大,以至于从观察者的视角就可以出现很明显的近大远小效果。从这种效果就可以知道,数据与真实值差别极为巨大。
近大远小的效果,只与y相关。
data['scale']= (y0+D)/(y+D)
遮挡效果
为了有真实感,行星之间、行星与轨道之间,轨道与太阳之间等等的遮挡效果是最关键的。
我们的做法是先画后半区,再画太阳,再画前半区。后半区中,远日行星先画;前半区中,近日行星先画。以保证正确的遮挡效果。
-
drawOrb(img, solar, posList,
0,
90,
True)
-
pasteSolor(img, solar, posList)
-
drawOrb(img, solar, posList,
90,
180,
False)
比较复杂一点的是行星与自身轨道之间的遮挡关系。必须实现一线穿一球的效果才好看。而且穿球位置不是固定不变的。这里,我们根据行星所在角度的不同,将轨道拆分为两半来画。一部分轨道是被行星遮挡的,另一部分轨道遮挡行星,但留一些空间,以实现比较自然的穿球效果。
-
drawArc(arc1, arc)
-
rate= posList[name][
'rate']* posList[name][
'scale']
-
pic= solarImg[name].resize(effect.tupleRound(effect.tupleMul(solarImg[name].size, rate)), Image.ANTIALIAS)
-
pos= effect.tupleRound(effect.tupleAdd(posList[name][
'pos'], effect.tupleMul(pic.size,
-0.5)))
-
r, g, b, alpha= pic.split()
-
img.paste(pic, pos, mask= alpha)
-
# 穿球点,随arc不同而不同
-
# 90度位置,在中心穿球,
-
# 越接近0或180度,越接近球边缘
-
# 根据这种性质,采用cos来模拟
-
darc= abs(round(math.cos(math.radians(arc))*solarImg[name].size[
1]*rate/
50))
-
# darc= abs(round(math.cos(math.radians(arc))*5))
-
# print(name, arc, darc)
-
drawArc(arc+darc, arc2)
素材下载地址:
链接:https://pan.baidu.com/s/18ELL4aL-jHbIbIacMpVbjA
提取码:5bjj
转载:https://blog.csdn.net/xiaorang/article/details/106884311