タンパク質の立体構造をSketchUpでモデル化 その6【ついでにDNAをモデル化】

#はじめに
シリース第6回です。

PDB形式のタンパク質立体構造データをもとに、タンパク質の立体構造モデルをSketchUpで作成するのがテーマのシリーズです。前回までに一応タンパク質の構造が作れるようになったということで、今回はDNAを描画する番外編です。

今回のデータは、MMDB ID:71816
のデータです。NCBIのサイトから入手しました。データは逆転写酵素と二本鎖DNAの複合体です。従ってファイルにはDNAのデータとタンパク質のデータが両方含まれています。

[タンパク質の立体構造をSketchUpでモデル化 その1](https://qiita.com/yoho/items/1e15bbd4e3a7c4c7ff69)
[タンパク質の立体構造をSketchUpでモデル化 その2【アミノ酸の側鎖を描画】](https://qiita.com/yoho/items/05037dd8f128c77152d7)
[タンパク質の立体構造をSketchUpでモデル化 その3【アミノ酸の側鎖も含めて描画】](https://qiita.com/yoho/items/3c8e5eb48d68324cec60)
[タンパク質の立体構造をSketchUpでモデル化 その4【主鎖を立体に】](https://qiita.com/yoho/items/d5626464fd39903e0d51)
[タンパク質の立体構造をSketchUpでモデル化 その5【玉の描画】](https://qiita.com/yoho/items/c85a37e9937ddbbaab9c)
[タンパク質の立体構造をSketchUpでモデル化 その6【ついでにDNAをモデル化】](https://qiita.com/yoho/items/0c38e1bca3b1ce17fbb5)
[タンパク質の立体構造をSketchUpでモデル化 その7【最終回】](https://qiita.com/yoho/items/a8204082a216e35462d7)

#DNAの部分のデータ
例えば塩基がTである2つ目のヌクレオチドの構造は以下のように書かれています。
だいたいタンパク質の時と同じですね。
3番目のカラムは、元素記号 + 原子番号
4番目のカラムは、塩基(A、C、G、T)の別
5番目のカラムは、ストランド名
6番目のカラムは、何番目のヌクレオチドか
のようです。

```
ATOM     18  P     T E   2       9.972  44.770  14.327  1.00100.00           P  
ATOM     19  O1P   T E   2       9.051  43.878  13.567  1.00100.00           O  
ATOM     20  O2P   T E   2       9.454  46.055  14.876  1.00100.00           O  
ATOM     21  O5*   T E   2      10.567  43.941  15.554  1.00100.00           O  
ATOM     22  C5*   T E   2      11.045  42.603  15.396  1.00 99.64           C  
ATOM     23  C4*   T E   2      10.346  41.683  16.372  1.00100.00           C  
ATOM     24  O4*   T E   2      10.332  42.346  17.664  1.00100.00           O  
ATOM     25  C3*   T E   2       8.882  41.384  16.042  1.00100.00           C  
ATOM     26  O3*   T E   2       8.562  39.978  16.160  1.00100.00           O  
ATOM     27  C2*   T E   2       8.076  42.336  16.911  1.00100.00           C  
ATOM     28  C1*   T E   2       9.002  42.679  18.075  1.00100.00           C  
ATOM     29  N1    T E   2       8.991  44.121  18.467  1.00 99.19           N  
ATOM     30  C2    T E   2       7.775  44.797  18.591  1.00 95.98           C  
ATOM     31  O2    T E   2       6.681  44.274  18.411  1.00 90.58           O  
ATOM     32  N3    T E   2       7.896  46.128  18.937  1.00 93.91           N  
ATOM     33  C4    T E   2       9.068  46.835  19.169  1.00 95.09           C  
ATOM     34  O4    T E   2       9.019  48.031  19.460  1.00 92.11           O  
ATOM     35  C5    T E   2      10.290  46.067  19.037  1.00 96.38           C  
ATOM     36  C5M   T E   2      11.601  46.745  19.283  1.00 95.47           C  
ATOM     37  C6    T E   2      10.191  44.771  18.697  1.00 97.99           C  
```
O1P、O2Pはリン酸基についた酸素原子、O5*は5'の炭素原子の隣の酸素原子、O4*はC4(そしてC1の)隣の炭素原子、ですね。 N1からC6までは塩基チミンの元素のはずです。各元素の番号はwikipediaを参照したらわかりました。

#DNAの結合情報の整理
シリーズ第3回と同じように、データを整理します。以下のようになりました。index 2と5のデータが、バックボーンです。

```rb
nucleotidesAndBonds = {
"A" => [["P","O1P"],["P","O2P"],["P","O5*","C5*","C4*","C3*"],["C3*","C2*","C1*"],["C4*","O4*","C1*"],["C3*","O3*"],["C1*","N9","C8","N7","C5","C6","N1","C2","N3","C4","N9"],["C4","C5"],["C6","N6"]],#A
"G" => [["P","O1P"],["P","O2P"],["P","O5*","C5*","C4*","C3*"],["C3*","C2*","C1*"],["C4*","O4*","C1*"],["C3*","O3*"],["C1*","N9","C8","N7","C5","C6","N1","C2","N3","C4","N9"],["C4","C5"],["C6","O6"],["C2","N2"]],#G
"C" => [["P","O1P"],["P","O2P"],["P","O5*","C5*","C4*","C3*"],["C3*","C2*","C1*"],["C4*","O4*","C1*"],["C3*","O3*"],["C1*","N1","C2","N3","C4","C5","C6","N1"],["C2","O2"],["C4","N4"]],#C
"T" => [["P","O1P"],["P","O2P"],["P","O5*","C5*","C4*","C3*"],["C3*","C2*","C1*"],["C4*","O4*","C1*"],["C3*","O3*"],["C1*","N1","C2","N3","C4","C5","C6","N1"],["C2","O2"],["C4","O4"],["C5","C5M"]],#T
}
```
アミノ酸の時は、主鎖と側鎖に分けて作成しました。これは主鎖だけを、色を塗るなどして表示できるようにしたかったからですが、これはDNAについても同じです。

##バックボーンデータを集める


```ruby
#DNAのバックボーンデータを整理
strands = []
strandIdentity = ""
for line in lines
    dataInLine = line.split
    if dataInLine[0] == "ATOM"
        if strandIdentity != dataInLine[4]
            points = []
            strands.push(points)
            strandIdentity = dataInLine[4]
        end
        if ["P","O5*","C5*","C4*","C3*","O3*","P"].include?(dataInLine[2])
            points.push(Geom::Point3d.new(dataInLine[6].to_f, dataInLine[7].to_f, dataInLine[8].to_f))
        end
    end
end
```
## 各モノマーのデータの整理

```rb
#A C G T関連の側鎖データをキープ
# hashを使用。ヌクレオチド_strand_ DNA番号をkey、配列をvalueにする。配列は行全体を要素とする。
nucleotidesAndLines = Hash.new{ |hash, key| hash[key] = [] }
for line in lines
    dataInLine = line.split
    if dataInLine[0] == "ATOM" and ["A","C","G","T"].include?(dataInLine[3]) then
        key = dataInLine[3..5].join("_")
        nucleotidesAndLines[key].push(line)
    end
end
```
## コード全体

```rb
# DNA用
#モデルを取得
model = Sketchup.active_model
entities = model.active_entities

#ファイルを読み込む
lines = IO.readlines("/Users/yoho/Downloads/mmdb_1D0E.pdb")

# 立体的なストランドを追加するメソッド。ストランドの半径と色を指定
def addStrand(strand,r,color)
    model = Sketchup.active_model
    entities = model.active_entities
    
    strandEdge = entities.add_edges(strand)
    #circleを追加する。
    vector = strand[0] - strand[1]
    circleEdges = entities.add_circle(strand[0], vector , r)
    #円をfaceに変換。
    face = entities.add_face(circleEdges)
    #faceにフォローミーツールを適用
    face.followme(strandEdge)
    #ストランドに色を付ける。entitiesの中から、フォローミーに使ったFaceの中心座標を含むFaceを探す。
    for x in entities #監修 @scivola 先生
        next unless x.typename == "Face"
        result = x.classify_point(strand[0])
        next unless result == Sketchup::Face::PointInside
        connected_entities = x.all_connected
        for y in connected_entities
            y.material= color if y.typename == "Face"
        end
    end
end

#使用する色のリスト
colors = ["SpringGreen","SlateBlue","MediumVioletRed","MediumBlue","Lime","LightSeaGreen","Indigo",
"Green","Gold","DodgerBlue","DarkOrchid","Crimson","Coral","RoyalBlue"]

#DNAのバックボーンデータを整理
strands = []
strandIdentity = ""
for line in lines
    dataInLine = line.split
    if dataInLine[0] == "ATOM"
        if strandIdentity != dataInLine[4]
            points = []
            strands.push(points)
            strandIdentity = dataInLine[4]
        end
        if ["P","O5*","C5*","C4*","C3*","O3*","P"].include?(dataInLine[2])
            points.push(Geom::Point3d.new(dataInLine[6].to_f, dataInLine[7].to_f, dataInLine[8].to_f))
        end
    end
end

#主鎖を描画edgeとして、追加
colorIndex = 0
for strand in strands
    next unless strand.size >=2
    addStrand(strand,0.1,colors[colorIndex%14])#立体的にしないなら entities.add_edges(strand)
    colorIndex += 1
end

#A C G T関連の側鎖データをキープ
# hashを使用。ヌクレオチド_strand_ DNA番号をkey、配列をvalueにする。配列は行全体を要素とする。
nucleotidesAndLines = Hash.new{ |hash, key| hash[key] = [] }
for line in lines
    dataInLine = line.split
    if dataInLine[0] == "ATOM" and ["A","C","G","T"].include?(dataInLine[3]) then
        key = dataInLine[3..5].join("_")
        nucleotidesAndLines[key].push(line)
    end
end


nucleotidesAndBonds = {
"A" => [["P","O1P"],["P","O2P"],["P","O5*","C5*","C4*","C3*"],["C3*","C2*","C1*"],["C4*","O4*","C1*"],["C3*","O3*"],["C1*","N9","C8","N7","C5","C6","N1","C2","N3","C4","N9"],["C4","C5"],["C6","N6"]],#A
"G" => [["P","O1P"],["P","O2P"],["P","O5*","C5*","C4*","C3*"],["C3*","C2*","C1*"],["C4*","O4*","C1*"],["C3*","O3*"],["C1*","N9","C8","N7","C5","C6","N1","C2","N3","C4","N9"],["C4","C5"],["C6","O6"],["C2","N2"]],#G
"C" => [["P","O1P"],["P","O2P"],["P","O5*","C5*","C4*","C3*"],["C3*","C2*","C1*"],["C4*","O4*","C1*"],["C3*","O3*"],["C1*","N1","C2","N3","C4","C5","C6","N1"],["C2","O2"],["C4","N4"]],#C
"T" => [["P","O1P"],["P","O2P"],["P","O5*","C5*","C4*","C3*"],["C3*","C2*","C1*"],["C4*","O4*","C1*"],["C3*","O3*"],["C1*","N1","C2","N3","C4","C5","C6","N1"],["C2","O2"],["C4","O4"],["C5","C5M"]],#T
}

# DNAを描画
nucleotidesAndLines.each{|key,value|
    #最初の行を分割したアレイのindex 3からヌクレオチド名を取得
    symbol = value[0].split[3]
    #それぞれの行にあるATOMの名前と、座標をHashにしまう。
    atomsAndCoordinates = Hash.new
    for x in value
        data = x.split
        atomsAndCoordinates[data[2]] = Geom::Point3d.new(data[6].to_f, data[7].to_f, data[8].to_f)
    end
    #ヌクレオチドごとに、描画すべき結合を、edgeとして追加する。
    #原子情報が欠けている場合があるので、bondsから2つずつ取り出して、対応する座標データが両方得られる場合に限って、edgeを追加する。
    list = nucleotidesAndBonds[symbol]
    for bonds in list
        for index in 0..bonds.size - 2 do
            atomA = atomsAndCoordinates[bonds[index]]
            atomB = atomsAndCoordinates[bonds[index+1]]
            if  atomA != nil and atomB != nil then
                entities.add_edges([atomA,atomB])
            else
                puts "結合情報がありません" + key +" "+ symbol + " "+ bonds[index] + "-" + bonds[index + 1]
            end
        end
    end
}
```
#描画結果
描画した結果です。

<img width="630" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595771/4283509b-51a3-86ce-b68c-6e853c5d2789.png">

#終わりに
DNAをモデル化できるようになったのは嬉しいですが、あんまりやらない方が良いことをしてしまいました。コードの一部だけをちょっとだけ変えて、他の機能にするというやつです。これでは、保守の手間が2倍になってしまいます。

もう少しRNAについてデータ形式を調べたら、タンパク質、DNA、RNAいずれのデータも描画できるように、機能をマージしたいと思います。おそらく、各ストランド(ポリマー)が何のストランドなのかを判別するようにして、判別結果に応じて集めるべき主鎖/バックボーン原子情報を指定することになると思います。

また、なるべくメインルーチンで書くのではなくて、機能単位でメソッド化するようにしたいと思います。