第2回ビネクラ杯のランキングが確定しました!

14-11. 見る方向によって変わる立体アートを作りたい

やること

見る方向によって変わる立体アートを作ってみたいです。こういうやつです↓。

Moving Sculpture

アルゴリズムさえ分かれば機械的に設計できますので、挑戦してみましょう。

実行環境

WinPython3.6をおすすめしています。

WinPython - Browse /WinPython_3.6/3.6.7.0 at SourceForge.net
Portable Scientific Python 2/3 32/64bit Distribution for Windows

Google Colaboratoryが利用可能です。

Google Colaboratory

素材

「V」「&」「C」の3文字を用意しました。41*41pxサイズのグレースケール画像です。

pip, import

必要なパッケージをインポートします。足りないと怒られたらpip installしてください。

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from PIL import Image

素材の読み込みと表示

素材を2枚指定して読み込みます。

  • 2枚の素材を白黒(bool)画像として読み込む
  • 画像のサイズを取得して「size」に格納する
  • 画像を表示する関数で表示する
#=======================
# パラメータ
#=======================
#画像パス
img_path_x = '14-11_v.png'
img_path_y = '14-11_and.png'


#=======================
# 表示関数
#=======================
def show_bool(img):
    plt.imshow(img, vmin = 0, vmax = 1)
    plt.show()
    plt.close()


#=======================
# 2枚の画像の読み込み
#=======================
#画像の読み込みと二値化
img_x = Image.open(img_path_x).convert('1'); img_x.close
img_y = Image.open(img_path_y).convert('1'); img_y.close
img_x = np.array(img_x)
img_y = np.array(img_y)

#サイズ取得
size = img_x.shape[0]
print('size:{}\n'.format(size))

#表示
show_bool(img_x)
show_bool(img_y)
size:41

「img_x」「img_y」はbool配列ですが、Pythonは親切な言語ですので、最小値 False(=0)を黄色、最大値 True(=1)を紫として表示してくれてます。

アートの作成

アルゴリズムはシンプルで、41*41*41サイズの立方体を用意して、x方向からは「V」の白い部分を削り込み、y方向からは「&」の白い部分を削り込みます。

なお、3Dグラフの表示には少し慣れが必要です。360度グルグル回転させながら表示するようにしています。

#=======================
# アートの作成
#=======================
#1埋めした3次元配列
data = np.ones((size, size, size), dtype=bool)

#1枚目の画像の白部分をx方向から削り込む
for i in range(size):
    data[:, i, :] *= np.rot90(img_x==False, -1) #ちょっと回したほうが見やすい
#2枚目の画像の白部分をy方向から削り込む
for i in range(size):
    data[i, :, :] *= np.rot90(img_y==False, -1) #ちょっと回したほうが見やすい

for i in range(0, 360):
    #表示
    fig = plt.figure(figsize=(6, 6))
    ax = Axes3D(fig)
    
    #要素に分解してプロット
    x, y, z = np.where(data==True)[:3]
    ax.plot(x, y, z, 'sk', markersize=5)
    
    #その他の設定
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')
    ax.set_xlim(0, size)
    ax.set_ylim(0, size)
    ax.set_zlim(0, size)
    
    #視点(垂直、水平)
    ax.view_init(0, 135 - i)
    
    #表示
    plt.show()
    plt.close()

こんな感じの画像がたくさん表示されます。

360枚をgifにするとこうなります。

「C」と「&」の場合

できました。

「V」「&」「C」を3方向から削り込んだ場合

できませんでした。各方向から見るとこうなっています。

3方向からきちんと見える立体は保証されないようです。

タイトルとURLをコピーしました