Calabi Yau 多様体を作ってみよう

#概要
記事のスペースが空いてましたので、書いております。箸休め的に読んでいただけると、、、
pythonの基礎がわかればいいなと思いまして、下の方に解説を書いております。
私自身の実力は、調べながらおっちらよっちらかける程度のものです。
そんな実力ですが、皆様の力になりますように。

calabi yau 多様体というあちら側の世界の図形を作ってみました。。
以下、詳細。
https://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%A9%E3%83%93%E3%83%BB%E3%83%A4%E3%82%A6%E5%A4%9A%E6%A7%98%E4%BD%93

ここのサイトに、Javaですがコードがのってましたので、、、
https://sw1227.hatenablog.com/entry/2018/12/03/235105

pythonで置き換えて、、、（使用したのは、pythonノードです。）
（vex使いたかったのですが、複素数の掛け算の方法が分からなかったので、てっとり早かったnumpyを使用しております）

#コード
```python
node = hou.pwd()
geo = node.geometry()
 
import numpy as np
import math
import cmath
 
geo.addAttrib(hou.attribType.Point, "count", 0)
geo.addAttrib(hou.attribType.Point, "k1c", 0)
geo.addAttrib(hou.attribType.Point, "k2c", 0)
geo.addAttrib(hou.attribType.Point, "xc", 0)
geo.addAttrib(hou.attribType.Point, "yc", 0)


#addAttribute
itr00 = hou.node('/obj/geo1/Control').parm('iteration').eval()
a = hou.node('/obj/geo1/Control').parm('a').eval()
itrPI = hou.node('/obj/geo1/Control').parm('iteration_pi').eval()


def addcalabiyau00(n,a):
    dx = $PI/itrPI
    dy = $PI/itrPI
    n = float(n)
    count = 0
    k1c = -1
    k2c = -1
    xc = -1
    yc = -1
    
    
    for k1 in drange(0.,n+1,1.):
        k1c += 1
        for k2 in drange(0.,n+1,1.):
            k2c += 1
            for x in drange(0,$PI/2+dx,dx):
                xc += 1
                for y in drange(-$PI/2,$PI/2+dy,dy):
                    yc += 1
                    print '-------------------------'
                    print k1,k2,x,y
                                 
                    date = [[x,y],[x+dx,y],[x+dx,y+dy],[x,y+dy]]
                    rels = []
                    ps = []
                    
                    
                    for d in date:
                        print d
                        #print d[0],d[1],n,k,k,a
                        rel = hou.Vector3(coordinate(d[0], d[1], n, k1, k2, a))
                        
                        print "k1",k1,"k2",k2 
                        print "x",x,"y",y
                        print "d.x",d[0],"d.y",d[1]
                        print rel
                        rels.append(rel)
                        point = geo.createPoint()
                        point.setPosition(rel)
                        point.setAttribValue("count", count)
                        
                        ps.append(point)
                                            
                    print 'ps',len(ps)
                    addTriangle(ps[0],ps[1],ps[2])
                    addTriangle(ps[0],ps[2],ps[3])
                    count += 1
 
#                print rels
#            print x
                
def coordinate(x, y, n, k1, k2, a):
    n = float(n)
    val11 = cmath.exp(complex(0,2*$PI*k1/n))
    val12 = pow(cmath.cos(complex(x,y)),2/n)
    z1 = val11*val12
    
    val21 = cmath.exp(complex(0,2*$PI*k2/n))
    val22 = pow(cmath.sin(complex(x, y)),2/n)
    z2 = val21*val22
 
    relx = z1.real
    rely = z2.real
    relz = z1.imag*math.cos(a) + z2.imag*math.sin(a)
    
    rel = hou.Vector3(relx,rely,relz)
    
    return rel
 
def drange(begin, end, step):
    n = begin
    while n+step < end:
     yield n
     n += step
     
# define a funciton to create triangels
def addTriangle(p,q,r):
 # create polygon of 3 corners
 f = geo.createPolygon()
 f.addVertex(p)
 f.addVertex(q)
 f.addVertex(r)
    
addcalabiyau00(itr00,a)    
print 'test',$FF
```
#結果
できた！
<img width="312" alt="スクリーンショット 2019-12-04 16.36.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540879/fbede266-f273-d325-bfa6-323562f55001.png">
正直あってるかわかりませんが、
パラメーター変更したら、ぽいのができたので、よしとしました！

さて、ここからは説明を時間の許す限り書いていこうと思います。

#pythonについて
##node = hou.pwd() geo = node.geometry() について
```python
node = hou.pwd()
geo = node.geometry()
```
pythonノードを作るとデフォルトで入っているコードです。
これは、pythonを書くときのお約束みたいなものです。ちゃんと調べてみると

>hou.pwd()はPython SOP自身のノードオブジェクトを返す。
Node.geometry()は自身に入力されたhou.Geometry型のジオメトリオブジェクトを返す。
geometryの中にはPointやEdgeやPrimitive型のオブジェクトが含まれており、実際に形状や色を変更する際は、これらのコンポーネントオブジェクトを取得して操作する
http://www.technical-artist.net/?p=241


とのことで、これによって諸々にアクセスできるようになるということですね。

##Importとは？
```python
import numpy as np
import math
import cmath
```
次の行のimport三連チャンです.
ライブラリを読み込んでいるものになります。さて、ライブラリとは？？
これもネットで調べてみます。ものを把握してはいるのですが、説明？どうしたらいいの？？な私なので、ネットの力を最大限活用したいと思います。

>ライブラリ
Pythonには「ライブラリ」という用語はないが、パッケージやモジュールのことをライブラリと呼ぶ場合もある。厳格に定義されたものではない。
https://note.nkmk.me/python-import-usage/

私は、まとまったツール群みたいな感覚で捉えています。

##geo.addAttrib(hou.attribType.Point, "count", 0)について
```python
geo.addAttrib(hou.attribType.Point, "count", 0)
geo.addAttrib(hou.attribType.Point, "k1c", 0)
geo.addAttrib(hou.attribType.Point, "k2c", 0)
geo.addAttrib(hou.attribType.Point, "xc", 0)
geo.addAttrib(hou.attribType.Point, "yc", 0)
```

さて、ここまで来るとhoudiniにアクセスするものになりますね。
関数という言い方で良いのかな。
何をしているかというと、、
ずばり **ポイントアトリビュート** を追加しています。

・geo（ジオメトリにアクセスしまっせ）
・addAttrib（アトリビュート追加しまっせ）
・hou.attribType.Point（追加するのは、ポイントアトリビュートでっせ）
・"count"（追加するアトリビュートの名前は、countでっせ）※""で囲むとストリング（文字列）になります。
・０（デフォルトの値でっせ）

はい、でっせでっせすみません、、私の頭の中はこんな感じです。

##Nullに作ったパラメーターから値を引っ張ってくる
```python
#addAttribute
itr00 = hou.node('/obj/geo1/Control').parm('iteration').eval()
a = hou.node('/obj/geo1/Control').parm('a').eval()
itrPI = hou.node('/obj/geo1/Control').parm('iteration_pi').eval()
```
まず、以下のように名前が Controlというヌルにパラメーターを追加しています。
追加の方法は、ノードの上で右クリックして
Parameters and Chnnels →　 Edit Parameter Interface で追加しています。
詳しくは、こちらの記事を。
https://qiita.com/MIN0NIUM/items/e23a5b3b62b344b621f5
<img width="565" alt="スクリーンショット 2019-12-04 17.43.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540879/83c7abbf-0b92-95c6-9375-82ec27b00800.png">
pythonは、型の宣言をしなくていいので、
そのまま下記のように値を変数に格納していきます。
itr00 = hou.node('/obj/geo1/Control').parm('iteration').eval()

さてさて、こちらの意味は何かというと、、、
hou.node('/obj/geo1/Control').parm('iteration').eval()
・hou.node('/obj/geo1/Control')（ノードの中のよう、/obj/geo1/Controlってのをみてくれ）
・parm('iteration')（パラメーターの名前が、iterationっていうのあるだろ）
・eval()（そいつの値だよ）

といった感じで、Controlノードのパラメーター名がiterationの値を引っ張ってきています。

##defって何？
```python
def addcalabiyau00(n,a):
```
defは、関数を定義できるやつです。
つまり、ここでは　**addcalabiyau00**　という引数が　**n** と **a** の関数を定義しています。

##addcalabiyau00って何してるの？
ここはちょっと長いのですが、やってることはfor文回してひたすら計算して、結果を元にgeometryを作っています。
計算の内容ですか？６次元を計算しているらしいですよ！
ホントの内容が知りたい方は、専門家にコンタクトをとって聞いてみてください。。
私には、分からない、、、です！

では、できる限り説明を、、
関数は４つばかし使っています。

・１個目(一番長い、メインになってるやつです。他の関数は、この関数の中で使われています。)

```python
def addcalabiyau00(n,a):
    dx = $PI/itrPI
    dy = $PI/itrPI
    n = float(n)
    count = 0
    k1c = -1
    k2c = -1
    xc = -1
    yc = -1
    
    
    for k1 in drange(0.,n+1,1.):
        k1c += 1
        for k2 in drange(0.,n+1,1.):
            k2c += 1
            for x in drange(0,$PI/2+dx,dx):
                xc += 1
                for y in drange(-$PI/2,$PI/2+dy,dy):
                    yc += 1
                    print '-------------------------'
                    print k1,k2,x,y
                                 
                    date = [[x,y],[x+dx,y],[x+dx,y+dy],[x,y+dy]]
                    rels = []
                    ps = []
                    
                    
                    for d in date:
                        print d
                        #print d[0],d[1],n,k,k,a
                        rel = hou.Vector3(coordinate(d[0], d[1], n, k1, k2, a))
                        
                        print "k1",k1,"k2",k2 
                        print "x",x,"y",y
                        print "d.x",d[0],"d.y",d[1]
                        print rel
                        rels.append(rel)
                        point = geo.createPoint()
                        point.setPosition(rel)
                        point.setAttribValue("count", count)
                        
                        ps.append(point)
                                            
                    print 'ps',len(ps)
                    addTriangle(ps[0],ps[1],ps[2])
                    addTriangle(ps[0],ps[2],ps[3])
                    count += 1
```

・２個目

```python
def coordinate(x, y, n, k1, k2, a):
    n = float(n)
    val11 = cmath.exp(complex(0,2*$PI*k1/n))
    val12 = pow(cmath.cos(complex(x,y)),2/n)
    z1 = val11*val12
    
    val21 = cmath.exp(complex(0,2*$PI*k2/n))
    val22 = pow(cmath.sin(complex(x, y)),2/n)
    z2 = val21*val22
 
    relx = z1.real
    rely = z2.real
    relz = z1.imag*math.cos(a) + z2.imag*math.sin(a)
    
    rel = hou.Vector3(relx,rely,relz)
    
    return rel
```

・3個目

```python
def drange(begin, end, step):
    n = begin
    while n+step < end:
     yield n
     n += step
```

・4個目

```python
# define a funciton to create triangels
def addTriangle(p,q,r):
 # create polygon of 3 corners
 f = geo.createPolygon()
 f.addVertex(p)
 f.addVertex(q)
 f.addVertex(r)
```

##for文
いっぱい使っているのが、for文
https://qiita.com/Morio/items/e8aed85346c0055beea7
このサイトを参考に解説してみたいと思います。
というか、読んだ方が早いかもですね。

さてさて、すごく簡単にいうとまさにこれ（上記のサイトに書いてあった）

```python

for 変数 in データの集まり:
    処理
```
>for文の流れは原則、「データの集まり」から、「データを一つずつ取り出す」という流れです

はい、ありがとうございます。こういうことです。
一つ一つ取り出してそれに処理を加えていくという流れになります！

一つ目の関数を例にしてみます。

```python
for k1 in drange(0.,n+1,1.):
```

という部分です。さて、いつもの感じ！？で解説いてみます。
上の式に置き換えると
**変数　→　k1 
データの集まり　→　drange(0.,n+1,1.)**
という感じになります。
いつもの通りに言うと、、

「drange(0.,n+1,1.)の中のよう奴ら、一塊ごとにk1にきな。
一塊ごとに、処理してやっから」

といった感じになります。
で、ここでややこしくしているのは、

```python

drange(0.,n+1,1.)
```
が、データの集まりだからです。これが何かというと、
3個目の関数になります。つまり下記の関数がデータの集まりをになっています。

```python

def drange(begin, end, step):
    n = begin
    while n+step < end:
     yield n
     n += step
```
では、中を覗いてみようと思います。
・n = begin
引数 bigin を　n　に代入しています。

##While文
・while n+step < end:
while文が出てきています。
では、下記のサイトを元に解説してみます。
https://www.javadrive.jp/python/for/index1.html

>while 条件式:
    条件式が真の時に実行する文

>while 条件式:
    条件式が真の時に実行する文1
    条件式が真の時に実行する文2
    条件式が真の時に実行する文3

どういうことかというと、
while n+step < end　の　n+step < end　の部分が成り立っている間は、下の部分の処理は実行してね！
という意味になります。

```python
while n+step < end:
     yield n
     n += step
```

は、n+step < end　が成り立っているときは、

```python
yield n
n += step
```
してね！という意味になります。

##yieldとは？
http://ailaby.com/yield/
上記のサイトから、yieldについてみていってみます。
解説を読むと、、なんか処理の軽くなるreturnという感じでしょうか。
returnは、関数の中から値を返してくれています。
つまり、毎回毎回 n という値を返してくれています。
で、そのあとで、　n += step　しています。　これは、毎回毎回 n に　引数 step を足して行っています。

##forとyieldとwhile
今回のコードをみてみると

```python
for k1 in drange(0.,n+1,1.)
```
```python
def drange(begin, end, step):
    n = begin
    while n+step < end:
     yield n
     n += step
```

なので、k1　という変数にdrange()で値をWhileが成り立っている間だけ、yieldで毎回毎回返すから
それに対して処理してねという、少し複雑な感じですが、こんな感じの処理をしています。

そうやってみていくと、

```python
    for k1 in drange(0.,n+1,1.):
        k1c += 1
        for k2 in drange(0.,n+1,1.):
            k2c += 1
            for x in drange(0,$PI/2+dx,dx):
                xc += 1
                for y in drange(-$PI/2,$PI/2+dy,dy):
                    yc += 1
                    print '-------------------------'
                    print k1,k2,x,y
                                 
                    date = [[x,y],[x+dx,y],[x+dx,y+dy],[x,y+dy]]
                    rels = []
                    ps = []
```
ここら辺までは大体同じ処理をしています。

```python
                        print d
                        #print d[0],d[1],n,k,k,a
                        rel = hou.Vector3(coordinate(d[0], d[1], n, k1, k2, a))
                        
                        print "k1",k1,"k2",k2 
                        print "x",x,"y",y
                        print "d.x",d[0],"d.y",d[1]
                        print rel
                        rels.append(rel)
                        point = geo.createPoint()
                        point.setPosition(rel)
                        point.setAttribValue("count", count)
                        
                        ps.append(point)
```
次の箇所をみると、大半が print です。
私が、ちゃんと動いているか不安に駆られて書いたprintです。

##print文
さて、print文が何をしてくれるかというと、、
その名の通り、printしてくれます。こんな感じです。
<img width="463" alt="スクリーンショット 2019-12-04 20.07.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540879/2fe5629b-ca3e-0c49-8cf4-647a8ff9f1ce.png">

つまり、forが動いていたら、値がどんどん変わっていくだろうということで、デバッグのために書いたPrint文です。
geometry操作は、していません。

てことで、print以外のところを書き出してみます。

```python
                        rel = hou.Vector3(coordinate(d[0], d[1], n, k1, k2, a))

                        rels.append(rel)
                        point = geo.createPoint()
                        point.setPosition(rel)
                        point.setAttribValue("count", count)
                        
                        ps.append(point)
```
という、実は非常に短いものになっています。

##hou.Vector3()って何？

HoduiniのHelpをみると
https://www.sidefx.com/ja/docs/houdini/hom/hou/Vector3.html
>数学的演算に関連した3つの浮動小数点の値のシーケンス
と、あります。

要は、三次元の値といった意味になります。

##coordinate(d[0], d[1], n, k1, k2, a)は何してるの？

さて、また関数が出てきました。
上に並べた二個目の関数ですね

```python

def coordinate(x, y, n, k1, k2, a):
    n = float(n)
    val11 = cmath.exp(complex(0,2*$PI*k1/n))
    val12 = pow(cmath.cos(complex(x,y)),2/n)
    z1 = val11*val12
    
    val21 = cmath.exp(complex(0,2*$PI*k2/n))
    val22 = pow(cmath.sin(complex(x, y)),2/n)
    z2 = val21*val22
 
    relx = z1.real
    rely = z2.real
    relz = z1.imag*math.cos(a) + z2.imag*math.sin(a)
    
    rel = hou.Vector3(relx,rely,relz)
    
    return rel
```
何をしているかというと、、、一言でいうと計算しています。

`*n = float(n)*
ここは、n をfloatに変換しています。

*val11 = cmath.exp(complex(0,2*$PI*k1/n))*
*val12 = pow(cmath.cos(complex(x,y)),2/n)*
複素数の計算です。
import で読み込んだ numpy の cmath というのを使って、参考サイトと同じになるように計算しています。
といった感じで計算しています。
で、最後z軸に複素数の値が現実の値として出せるよう変換して
return で 先ほど出てきた　hou.Vector3　の値を返しています。

さて、そろそろリミット時間になってきました。すみません、また時間のあるときに足していければと思います。
間違っていたらすみません、ご指摘をお願いいたします。また、私もpythonの熟練者ではなく調べながらここまでたどりついたので、より簡単にかける方法があるなら教えていただきたいです。
ではでは、誰かのpythonのとっかかりになてくれれば幸いです。