用python繪出一個莫比烏斯環

昨天寫了一篇subplot的練習放到微信公衆賬號中,想用一個莫比烏斯環的圖片作爲封面圖片,就去維基百科上扒了一個。但是分辨率很低,就想自己繪出一個來。沒想到越陷越深,就一起把3D繪圖給學習了一遍。

(我昨天在CSDN發表了這篇文章,今天竟然找不到了!)

源代碼:https://github.com/gt11799/mobiusband

有關莫比烏斯帶,請戳維基百科http://zh.wikipedia.org/wiki/%E8%8E%AB%E6%AF%94%E4%B9%8C%E6%96%AF%E5%B8%A6

函數已經給出,剩下的就是取值,繪圖。要繪3D圖,X,Y,Z都得是矩陣。平常的做法就是遍歷,給矩陣中的每一個元素賦值,如果取樣點爲100,那麼計算X的賦值次數就是100*100,每次賦值還要計算兩次cos()。

這就要請出今天的明星-numpy。

numpy是python中的高效數值運算模塊。其中包含了各種數學運算,以及array數據類型,這些都是用C寫的,如果只是進行少量數值的運算,numpy的性能是低於math的,但是大批量的數值運算,numpy的優勢就體現出來了。

首先介紹的是array數據類型,很像list,但是可以不用迭代,直接運算。

>>> from numpy import *

>>> a = array([1,2,3,4])

>>> a * 3

array([ 3,  6,  9, 12])

這樣就可以直接把取樣點直接投到函數中得到X的值。

取樣也很方便,比如在這個例子中,我們可以這麼取v和u:

 v = linspace(-1.0, 1.0, num=500, endpoint=True)  u = linspace(0, 2*pi, num=500, endpoint=True)

參數分別是(start,stop,number of sample, 是否包含終止數值)

但是array沒有append或者add方法,而且,我試了很久,也沒有辦法把數據一行一行的添加到一個array中,於是我找了list當作中間人。

把運算複雜度高的u迭代,每次計算出全部的v。

for value_u in u:

answer.append((1. + v / 2. * cos(value_u/2.)) * cos(value_u))

要提前把v和u取成array類型,要提前定義answer是list。更高效的是直接在list中迭代,我們可以爲X,Y,Z分別建立函數,返回數值array。

def func_x(v, u):     answer = [((1. + v / 2. * cos(value_u/2.)) * cos(value_u)) for value_u in u]     return array(answer)

其他座標軸依此類推。

我們已經準備好了所需要材料,就等着下鍋了。3Dplot我用的是mpl_toolkits.mplot中的Axes3D模塊,這是matplotlib中的一部分。繪圖也很簡單,我用的是其中的surface方法。

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
surf = ax.plot_surface(X, Y, Z)


先新建一個figure(爲了設置dpi),然後以添加子圖的形式添加進去就行。這樣就已經成型了,提高下dpi

fig = plt.figure(dpi=150)
增加點色彩變化

surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm)
需要從matplotlib中倒入cm,cm模塊很有趣,有着很多的色彩變化,可以參考官方文檔。我嘗試了多種,最後選擇是YlOrBr,是yellow or brand,效果圖最後會貼出來。


去掉那些黑色曲線

surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, linewidth=0)
然後還玩了一把透明度alpha,最後發現透明度低一點的好(alpha越小,透明度越高)

surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, linewidth=0, alpha=0.9)
現在的圖z軸太高,造成立體感不明顯,於是我們拉長z軸,讓圖扁平一點。

ax.set_zlim(-2., 2.)

然後去掉座標軸

plt.axis('off')
如果用show()方法顯示圖表,然後保存,背景不是透明的,我們可以用figure.save()取保存

fig.savefig('mobiusband.png', transparent=True)
發現圖不夠清晰,增加dpi

fig.savefig('mobiusband.png', transparent=True, dpi=600)
然後就得到了最終的圖了


——————————————

github主頁:https://github.com/gt11799 

E-mail:[email protected]



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章