5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python turtleでカーボンナノチューブを描きたい

Last updated at Posted at 2019-09-11

Python の turtle モジュールを用いると、手軽に描画を行うことができます。
プログラムが正確に描画を進めてくれるため、絵に自信がない方でも安心して描けますね。

このモジュールを試してみようと思い、友人にお題を尋ねたところ「カーボンナノチューブ」と返ってきました(もう少し簡単なものが良かったです...)。
せっかくなので、その際に作成したプログラムを紹介します。
なお、本プログラムはあくまで描画を目的としたものであり、構造計算などは行っていません。

↓こんな感じで描きあがります。
2019-09-11 (3).png

描画プログラム

以下がプログラム全文です。

carbon_nano_tube.py
import numpy as np
import turtle as tr
import math

# 2次元回転行列
def rotate_2d(x0,y0,deg):
    x=x0*np.cos(deg)-y0*np.sin(deg)
    y=x0*np.sin(deg)+y0*np.cos(deg)
    return x,y

# 3次元回転行列
def rotate_3d(vec0, rol=None, yaw=None, pit=None):
    mat=np.array([[1.,0.,0.],[0.,1.,0.],[0.,0.,1.]])
    if(pit is not None):
        mat=np.dot(mat,np.array([[1.,0.,0.],[0.,np.cos(pit),-np.sin(pit)],[0.,np.sin(pit),np.cos(pit)]]))
    if(yaw is not None):
        mat=np.dot(mat,np.array([[np.cos(yaw),0.,np.sin(yaw)],[0.,1.,0.],[-np.sin(yaw),0.,np.cos(yaw)]]))
    if(rol is not None):
        mat=np.dot(mat,np.array([[np.cos(rol),-np.sin(rol),0.],[np.sin(rol),np.cos(rol),0.],[0.,0.,1.]]))
    vec=np.dot(mat,vec0)
    return vec

# 与えられた半径の円を描く。同じ段の他の原子と比べて奥行きが奥なら青、手前なら赤で円を描く。
def maru(r,coordinate,now_i):
    hed=tr.heading()
    pos=tr.pos()
    ave=0
    warp(pos[0],pos[1]-r)
    tr.seth(0.0)
    for i in range(atoms):
        ave += coordinate[i][2]/atoms
    if(coordinate[now_i][2] > ave):
        tr.color('Blue')
    else:
        tr.color('Red')
    tr.circle(r)
    tr.color('Black')
    warp(pos[0],pos[1])
    tr.seth(hed)

# 線を引かずに場所を移動する。
def warp(x,y):
    tr.pu()
    tr.goto(x,y)
    tr.pd()
    
############## parameters ############
# rol, yaw, pitの回転方向はそれぞれ画面手前、下、左への軸に対して右手系。
atoms =32           #チューブ断面当たりの炭素原子数
steps =50           #チューブ断面の段数
space=30             #炭素原子同士の間隔
rol=0               
yaw=np.pi/9
pit=-np.pi/12

######################################
splits=atoms/2*3        
tube=np.array([[[0.,0.,j*space/2*math.sqrt(3)] for i in range(atoms)] for j in range(steps)])
ix=0
iy=space/2/np.sin(2*np.pi/splits/2)
iz=0
tr.reset()
tr.speed(0)
screen = tr.Screen()
screen.delay(0)

# 2次元回転行列を利用して、各段の炭素原子の座標を決定していく。
for j in range(steps):
    vec=np.array([ix,iy,iz])    #座標指定用の位置ベクトル
    if(j%2==0):
        tube[j][0] = tube[j][0] + vec
        for i in range(atoms-1):
            if(i%2==0):
                vec[0],vec[1] = rotate_2d(vec[0],vec[1],-2*np.pi/splits*2)           
            else:
                vec[0],vec[1] = rotate_2d(vec[0],vec[1],-2*np.pi/splits)
            tube[j][i+1] = tube[j][i+1] + vec
    else:
        vec[0],vec[1] = rotate_2d(vec[0],vec[1],-2*np.pi/splits/2)
        tube[j][0] = tube[j][0] + vec
        for i in range(atoms-1):
            if(i%2==0):
                vec[0],vec[1] = rotate_2d(vec[0],vec[1],-2*np.pi/splits)
            else:
                vec[0],vec[1] = rotate_2d(vec[0],vec[1],-2*np.pi/splits*2)
            tube[j][i+1] = tube[j][i+1] + vec

# 設定された回転角に応じてそれぞれの炭素原子座標をを3次元回転。
for j in range(steps):
    for i in range(atoms):
        tube[j][i]=rotate_3d(tube[j][i],rol ,yaw, pit)

# 炭素原子の位置で丸を描き、接続する炭素原子へ直線を描く。
for j in range(steps-1):
    for i in range(atoms):
        warp(tube[j][i][0],tube[j][i][1])
        maru(5,tube[j],i)
        tr.goto(tube[j+1][i][0],tube[j+1][i][1])
        warp(tube[j][i][0],tube[j][i][1])
        if(j%2==0):
            if(i%2==0):
                if(i==0):
                    tr.goto(tube[j][atoms-1][0],tube[j][atoms-1][1])
                else:
                    tr.goto(tube[j][i-1][0],tube[j][i-1][1])
        else:
            if(i%2==0):
                tr.goto(tube[j][i+1][0],tube[j][i+1][1])

input('Finish! Please put Enter Key')     #待機用Input
  

チューブの長さや角度、炭素原子同士の間隔等はプログラム中のパラメータを変えることで任意に変更できます。
また、このプログラムでは各炭素原子の座標を配列に入れておき、座標を回転させたり、線で繋いだりしてチューブを描画しています。

処理の流れ

全体の処理の流れとしては、

  1. 3次元の座標を入れる[x,y,z]を、段数 × チューブ断面当たりの原子の個数で用意(3次元配列で作成)。

  2. 設定した原子の間隔や個数から、それぞれの炭素原子の座標を計算。

  3. 設定した3次元の回転角(rol, yaw, pit)から、すべての原子を原点を中心に回転(チューブが回転)。

  4. 各原子の位置で丸を描画、同時に接続する原子に向かって線を描画。(この時に各原子の奥行に応じて丸の色を変えている。)

以上です。けっこう綺麗に描くことができました。

参考リンク

ベクトルの内積や行列の積を求めるnumpy.dot関数の使い方

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?