亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

使用python怎么模擬在天空中放風箏

發布時間:2021-05-07 15:10:12 來源:億速云 閱讀:290 作者:Leah 欄目:開發技術

使用python怎么模擬在天空中放風箏?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

python可以做什么

Python是一種編程語言,內置了許多有效的工具,Python幾乎無所不能,該語言通俗易懂、容易入門、功能強大,在許多領域中都有廣泛的應用,例如最熱門的大數據分析,人工智能,Web開發等。

pip install numpy
pip install scipy
pip install pillow
pip install wxgl

NumPy和pillow是Python旗下最常用的科學計算庫和圖像處理庫,屬于常用模塊。WxGL是一個基于PyOpenGL的三維數據可視化庫,以wx為顯示后端,提供Matplotlib風格的交互式應用模式,同時,也可以和wxPython無縫結合,在wx的窗體上繪制三維模型。關于WxGL的更多信息,請參閱我的另一篇博客《十分鐘玩轉3D繪圖:WxGL完全手冊》。

2.2 草原和風箏素材

請下載下面的草原和風箏素材,保存到項目路徑下的res文件夾中。如果使用其他圖片,請保持草原圖片的寬高比為4:3,風箏素材需要帶透明通道的png格式。
草原素材:sky.jpg

使用python怎么模擬在天空中放風箏

風箏素材:butterfly.jpg

使用python怎么模擬在天空中放風箏

風箏素材:eagle.jpg

使用python怎么模擬在天空中放風箏

風箏素材:fish.jpg

使用python怎么模擬在天空中放風箏

2.3 打開IDLE,導入模塊

>>> import numpy as np
>>> from PIL import Image
>>> import wxgl.wxplot as plt # 交互式3D繪圖庫
>>> from scipy.spatial.transform import Rotation # 空間旋轉計算

3 制作工序

3.1 藍天和草原

用3D繪制天空,最常用的方法是天空頂和天空盒。不過,這兩個方法都有局限性,效果只能說差強人意。我們這里用的是天空盒。所謂天空盒,顧名思義,就是從一張圖片上裁切出六個矩形,拼成一個六面體,觀察者站在六面體內,就有了“天蒼蒼野茫茫”的趕腳。

使用python怎么模擬在天空中放風箏

下圖是從上圖裁切出的上下前后左右六個面。

使用python怎么模擬在天空中放風箏

了解了天空盒的原理,實現起來就簡單多了。先來裁切上下前后左右六個面。

>>> im = np.array(Image.open(r'D:\temp\kite\res\sky.jpg')) # 打開藍天草原的圖片
>>> u = im.shape[0]//3 # 天空盒(正六面體的棱長)
>>> im_top = im[:u, u:2*u, :]
>>> im_left = im[u:2*u, :u, :]
>>> im_front = im[u:2*u, u:2*u, :]
>>> im_right = im[u:2*u, 2*u:3*u, :]
>>> im_back = im[u:2*u, 3*u:, :]
>>> im_bottom = im[2*u:, u:2*u, :]

再生成立方體的六個面在三維空間中的坐標,其中每個面用四個頂點表示,頂點按逆時針方向排列。立方體的棱長為2,也就是xyzz坐標都在[-1,1]范圍內。

>>> vs_front = np.array([[-1,-1,1], [-1,-1,-1], [-1,1,-1], [-1,1,1]])
>>> vs_left = np.array([[1,-1,1], [1,-1,-1], [-1,-1,-1], [-1,-1,1]])
>>> vs_right = np.array([[-1,1,1], [-1,1,-1], [1,1,-1], [1,1,1]])
>>> vs_top = np.array([[1,-1,1], [-1,-1,1], [-1,1,1], [1,1,1]])
>>> vs_bottom = np.array([[-1,-1,-1], [1,-1,-1], [1,1,-1], [-1,1,-1]])
>>> vs_back = np.array([[1,-1,1], [1,-1,-1], [1,1,-1], [1,1,1]])

有了六個面的材質和頂點,就可以使用surface函數繪制天空盒了。

>>> plt.surface(vs_front, texture=im_front, alpha=False)
>>> plt.surface(vs_left, texture=im_left, alpha=False)
>>> plt.surface(vs_right, texture=im_right, alpha=False)
>>> plt.surface(vs_top, texture=im_top, alpha=False)
>>> plt.surface(vs_bottom, texture=im_bottom, alpha=False)
>>> plt.surface(vs_back, texture=im_back, alpha=False)
>>> plt.show()

咦?不對啊,為什么我在天空盒外而不是天空盒內呢?

使用python怎么模擬在天空中放風箏

原來,WxGL默認觀察者距離坐標原點5個單位的距離,而天空盒在[-1,1]范圍內,自然就處于天空盒外了。莫著急,只要設置一下畫布函數plt.figure()的參數,就OK了。參數dist用于設置觀察者距離觀察目標的距離,配合方位角參數azimuth和仰角參數elevation,可以確定觀察者位置;參數view用于設置視景體,view數組的6個元素分別表示視景體的左、右、上、下面,以及前后面距離觀察者的距離。

>>> plt.figure(dist=0.8, view=[-1, 1, -1, 1, 0.8, 7], elevation=0, azimuth=0)
>>> plt.surface(vs_front, texture=im_front, alpha=False)
>>> plt.surface(vs_left, texture=im_left, alpha=False)
>>> plt.surface(vs_right, texture=im_right, alpha=False)
>>> plt.surface(vs_top, texture=im_top, alpha=False)
>>> plt.surface(vs_bottom, texture=im_bottom, alpha=False)
>>> plt.surface(vs_back, texture=im_back, alpha=False)
>>> plt.show()

天空盒最終的效果如下圖所示。嘗試拖動鼠標、滑動滾輪,你會發現天空盒的缺陷。不過,這不會影響我們放飛風箏。

使用python怎么模擬在天空中放風箏

為了方便后續操作,我們將繪制天空盒的代碼封裝成一個函數。

>>> def draw_sky_box():
        plt.surface(vs_front, texture=im_front, alpha=False)
        plt.surface(vs_left, texture=im_left, alpha=False)
        plt.surface(vs_right, texture=im_right, alpha=False)
        plt.surface(vs_top, texture=im_top, alpha=False)
        plt.surface(vs_bottom, texture=im_bottom, alpha=False)
        plt.surface(vs_back, texture=im_back, alpha=False)
>>>

3.2 第一只風箏

現在觀察者位于(0.8,0,0)的位置,假定風箏中心位于v1點(-0.5,-0.3,0.2)的位置(觀察者左前上方)。我們需要根據風箏素材的尺寸,確定風箏在空間中的坐標。

>>> im_kite = np.array(Image.open(r'D:\temp\kite\res\butterfly.png')) # 打開風箏圖片
>>> max_s = max(im_kite.shape) # 風箏的最長邊
>>> dx, dy = 0.1*im_kite.shape[0]/max_s, 0.1*im_kite.shape[1]/max_s # 計算風箏在空間中的實際尺寸
>>> v1 = (-0.5,-0.3,0.2) # 風箏中心位置
>>> vs_kite = np.array([[dx,-dy,0.03], [-dx,-dy,0], [-dx,dy,0], [dx,dy,0.03]]) # 風箏四角的坐標,前端略高(后仰0.03)
>>> vs_kite[:,0] += v1[0] # 從原點移到v1點
>>> vs_kite[:,1] += v1[1] # 從原點移到v1點
>>> vs_kite[:,2] += v1[2] # 從原點移到v1點
>>> plt.figure(dist=0.8, view=[-1, 1, -1, 1, 0.8, 7], elevation=0, azimuth=0) # 設置畫布
>>> draw_sky_box() # 繪制天空盒
>>> plt.surface(vs_kite, texture=im_kite, alpha=True) # 繪制風箏(png格式需要使用透明通道)
>>> plt.show()

至此,終于在草原上放飛了第一只風箏。

使用python怎么模擬在天空中放風箏

3.3 給風箏加上線

風箏線近似于一條懸鏈線,我們可以用三次曲線模擬。如果放風箏的人在v0點,風箏中心位于v1點,風箏線就可以用k個點來描述。先來定義一個根據v0點和v1點計算風箏線的函數。

>>> def get_line(v0, v1, k=300):
        m = np.power(np.linspace(0,k,k), 3)/(k*k*k)
        dx, dy = v1[0]-v0[0], v1[1]-v0[1]
        x = v1[0] - m*dx
        y = v1[1] - m*dy
        z = np.linspace(v1[2], v0[2], k)
        return x, y, z
>>>

重復一遍繪制天空盒和風箏的代碼,稍加修改,即可加上風箏線。

>>> v0 = (0.5,0.2,-1) # 放風箏的人在v0點
>>> v1 = (-0.5,-0.3,0.2) # 風箏中心位于v1點
>>> xs, ys, zs = get_line(v0, v1) # 計算風箏懸鏈線
>>> plt.figure(dist=0.8, view=[-1, 1, -1, 1, 0.8, 7], elevation=0, azimuth=0) # 設置畫布
>>> draw_sky_box() # 繪制天空盒
>>> plt.surface(vs_kite, texture=im_kite, alpha=True) # 繪制風箏
>>> plt.plot(xs, ys, zs, color='#C0C0C0', width=0.3) # 繪制風箏懸鏈線
>>> plt.show()

plt.plot()函數用于繪制點或線,參數width用于設置線寬。如果覺得風箏線不夠明顯,可以適當增加線寬。

使用python怎么模擬在天空中放風箏

3.4 讓風箏動起來

想象一下風箏在天空中的飄動姿態,其運動軌跡有兩個特點:

水平方向延弧線擺動,幅度約30°左右

擺動到左側則左側稍低,擺動到右側則右側稍低

據此,不難模擬出風箏的擺動軌跡,計算出運動軌跡線上每一處風箏的坐標,同時計算出對應的風箏懸鏈線。啟動一個定時器,順序顯示軌跡線上每一處風箏及其懸鏈線,形成動畫。

WxGL的plt.surface()函數和plt.plot()函數,支持通過參數slide=True將對應的模型放入一個動畫序列,執行plt.show()的時候,會自動播放這個模型序列,時間間隔由plt.figure()函數的interval參數決定,默認值100毫秒。如果多個模型需要同時顯示,只需要用name參數為多個模型指定相同的名字即可。

好,我們來定義一個繪制飄動風箏的函數。

>>> def draw_kite(fn, v0, v1, dh=0.03, ex=(-20,20), fs=160):
        im_kite = np.array(Image.open(fn)) # 打開風箏圖片
        max_s = max(im_kite.shape) # 風箏的最長邊
        dx, dy = 0.1*im_kite.shape[0]/max_s, 0.1*im_kite.shape[1]/max_s # 計算風箏在空間中的實際尺寸    
        delta = np.hstack((np.linspace(-0.03, 0.03, fs), np.linspace(0.03, -0.03, fs))) # 風箏左右擺動過程中的高度波動
        theta = np.hstack((np.linspace(ex[0], ex[1], fs), np.linspace(ex[1], ex[0], fs))) # 風箏左右擺動的角度
        vs_kite = np.array([[dx,-dy,dh], [-dx,-dy,0], [-dx,dy,0], [dx,dy,dh]]) # 風箏四角的坐標,前端略高(后仰)
        vs_kite[:,0] += v1[0]
        vs_kite[:,1] += v1[1]
        vs_kite[:,2] += v1[2]    
        offset = np.random.randint(0, 2*fs)
        for i in range(2*fs):
            k = (i+offset)%(2*fs)
            rotator = Rotation.from_euler('xyz', [0, 0, theta[k]], degrees=True)
            vs = rotator.apply(vs_kite)
            vs[:2, 2] -= delta[k]
            vs[2:, 2] += delta[k]
            plt.surface(vs, texture=im_kite, alpha=True, slide=True, name='id_%d'%i)
            xs, ys, zs = get_line(v0, ((vs[0][0]+vs[2][0])/2,(vs[0][1]+vs[2][1])/2,(vs[0][2]+vs[2][2])/2))
            plt.plot(xs, ys, zs, color='#C0C0C0', width=0.3, slide=True, name='id_%d'%i)    
>>>

調用一下試試看。

>>> plt.figure(dist=0.8, view=[-1, 1, -1, 1, 0.8, 7], elevation=0, azimuth=0, interval=50) # 設置畫布,動畫間隔50毫秒
>>> draw_sky_box() # 繪制天空盒
>>> draw_kite(r'D:\temp\kite\res\butterfly.png', (0.5,0.2,-1), (-0.5,-0.3,0.2)) # 繪制風箏
>>> plt.show()

和我們設想的一樣,風箏在[-20°,20°]的范圍內左右擺動,懸鏈線也跟著一起飄動。

使用python怎么模擬在天空中放風箏

3.5 放飛更多的風箏

現在,我們有三張風箏的圖片,把它們都放飛到天空盒中吧。至于風箏的位置、放飛者的位置,你可以根據自己的想象,隨意定義。

>>> plt.figure(dist=0.8, view=[-1, 1, -1, 1, 0.8, 7], elevation=0, azimuth=0, interval=50)
>>> draw_sky_box()
>>> draw_kite(r'D:\temp\kite\res\butterfly.png', (0.5,0.2,-1), (-0.5,-0.3,0.2))
>>> plt.show()
>>> plt.figure(dist=0.8, view=[-1, 1, -1, 1, 0.8, 7], elevation=0, azimuth=0, interval=50)
>>> draw_sky_box()
>>> draw_kite(r'D:\temp\kite\res\butterfly.png', (0.5,0.2,-1), (-0.5,-0.3,0.2))
>>> draw_kite(r'D:\temp\kite\res\fish.png', (0.3,0,-1), (-0.2,-0.1,0.05), ex=(-40,40))
>>> draw_kite(r'D:\temp\kite\res\eagle.png', (0.2,0.05,-1), (-0.6,0.5,0.35))
>>> plt.show()

至此,大功告成。

使用python怎么模擬在天空中放風箏

4 完整源碼

# -*- coding: utf-8 -*-

import numpy as np
from PIL import Image
import wxgl.wxplot as plt # 交互式3D繪圖庫
from scipy.spatial.transform import Rotation # 空間旋轉計算

def draw_sky_box(fn):
    """繪制天空盒

    fn      - 圖片文件名(寬高比4:3)
    """

    im = np.array(Image.open(fn)) # 打開資源圖片
    u = im.shape[0]//3 # 天空盒(正六面體的棱長)

    # 裁切出天空盒6個面:上下前后左右
    im_top = im[:u, u:2*u, :]
    im_left = im[u:2*u, :u, :]
    im_front = im[u:2*u, u:2*u, :]
    im_right = im[u:2*u, 2*u:3*u, :]
    im_back = im[u:2*u, 3*u:, :]
    im_bottom = im[2*u:, u:2*u, :]

    # 定義天空盒六個面的頂點坐標,4個頂點按逆時針方向排列
    vs_front = np.array([[-1,-1,1], [-1,-1,-1], [-1,1,-1], [-1,1,1]])
    vs_left = np.array([[1,-1,1], [1,-1,-1], [-1,-1,-1], [-1,-1,1]])
    vs_right = np.array([[-1,1,1], [-1,1,-1], [1,1,-1], [1,1,1]])
    vs_top = np.array([[1,-1,1], [-1,-1,1], [-1,1,1], [1,1,1]])
    vs_bottom = np.array([[-1,-1,-1], [1,-1,-1], [1,1,-1], [-1,1,-1]])
    vs_back = np.array([[1,-1,1], [1,-1,-1], [1,1,-1], [1,1,1]])

    # 繪制天空盒的六個面
    plt.surface(vs_front, texture=im_front, alpha=False)
    plt.surface(vs_left, texture=im_left, alpha=False)
    plt.surface(vs_right, texture=im_right, alpha=False)
    plt.surface(vs_top, texture=im_top, alpha=False)
    plt.surface(vs_bottom, texture=im_bottom, alpha=False)
    plt.surface(vs_back, texture=im_back, alpha=False)

def get_line(v0, v1, k=300):
    """風箏線:從風箏底部到放飛者,近似懸鏈線

    v0      - 放飛者坐標
    v1      - 風箏底部系線處坐標
    k       - 描繪風箏線的點的數量,默認300點
    """

    m = np.power(np.linspace(0,k,k), 3)/(k*k*k)
    dx, dy = v1[0]-v0[0], v1[1]-v0[1]
    x = v1[0] - m*dx
    y = v1[1] - m*dy
    z = np.linspace(v1[2], v0[2], k)

    return x, y, z

def draw_kite(fn, v0, v1, dh=0.03, ex=(-20,20), fs=160):
    """繪制風箏

    fn      - 風箏圖片文件名(png格式,帶透明通道)
    dh      - 風箏后仰高度,默認0.02
    ex      - 風箏左右擺動的角度范圍
    fs      - 風箏隨風擺動的幀數
    """

    im_kite = np.array(Image.open(fn)) # 打開風箏圖片
    max_s = max(im_kite.shape) # 風箏的最長邊
    dx, dy = 0.1*im_kite.shape[0]/max_s, 0.1*im_kite.shape[1]/max_s # 計算風箏在空間中的實際尺寸

    delta = np.hstack((np.linspace(-0.03, 0.03, fs), np.linspace(0.03, -0.03, fs))) # 風箏左右擺動過程中的高度波動
    theta = np.hstack((np.linspace(ex[0], ex[1], fs), np.linspace(ex[1], ex[0], fs))) # 風箏左右擺動的角度

    vs_kite = np.array([[dx,-dy,dh], [-dx,-dy,0], [-dx,dy,0], [dx,dy,dh]]) # 風箏四角的坐標,前端略高(后仰)
    vs_kite[:,0] += v1[0]
    vs_kite[:,1] += v1[1]
    vs_kite[:,2] += v1[2]

    offset = np.random.randint(0, 2*fs)
    for i in range(2*fs):
        k = (i+offset)%(2*fs)
        rotator = Rotation.from_euler('xyz', [0, 0, theta[k]], degrees=True)
        vs = rotator.apply(vs_kite)
        vs[:2, 2] -= delta[k]
        vs[2:, 2] += delta[k]
        plt.surface(vs, texture=im_kite, alpha=True, slide=True, name='id_%d'%i)

        xs, ys, zs = get_line(v0, ((vs[0][0]+vs[2][0])/2,(vs[0][1]+vs[2][1])/2,(vs[0][2]+vs[2][2])/2))
        plt.plot(xs, ys, zs, color='#C0C0C0', width=0.3, slide=True, name='id_%d'%i)

if __name__ == '__main__':
    plt.figure(dist=0.8, view=[-1, 1, -1, 1, 0.8, 7], elevation=0, azimuth=0, interval=50)
    draw_sky_box('res/sky.jpg')
    draw_kite('res/butterfly.png', (0.5,0.2,-1), (-0.5,-0.3,0.2))
    draw_kite('res/fish.png', (0.3,0,-1), (-0.2,-0.1,0.05), ex=(-40,40))
    draw_kite('res/eagle.png', (0.2,0.05,-1), (-0.6,0.5,0.35))
    plt.show()

看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

永登县| 漳州市| 洛隆县| 洛宁县| 广元市| 平凉市| 秭归县| 克东县| 罗平县| 凤台县| 八宿县| 长泰县| 遵义市| 微博| 巫溪县| 乌什县| 上蔡县| 西宁市| 岳西县| 漳浦县| 光山县| 昭觉县| 延边| 屏东市| 玉树县| 邮箱| 峨山| 古浪县| 监利县| 林甸县| 恭城| 丹寨县| 岳西县| 兴海县| 商城县| 临朐县| 嫩江县| 色达县| 丰镇市| 雷州市| 霍城县|