Help us understand the problem. What is going on with this article?

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を使用しております)

コード

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

結果

できた!
スクリーンショット 2019-12-04 16.36.45.png
正直あってるかわかりませんが、
パラメーター変更したら、ぽいのができたので、よしとしました!

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

pythonについて

node = hou.pwd() geo = node.geometry() について

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とは?

import numpy as np
import math
import cmath

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

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

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

geo.addAttrib(hou.attribType.Point, "count", 0)について

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でっせ)※""で囲むとストリング(文字列)になります。
・0(デフォルトの値でっせ)

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

Nullに作ったパラメーターから値を引っ張ってくる

#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
スクリーンショット 2019-12-04 17.43.04.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って何?

def addcalabiyau00(n,a):

defは、関数を定義できるやつです。
つまり、ここでは addcalabiyau00 という引数が na の関数を定義しています。

addcalabiyau00って何してるの?

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

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

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

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

・2個目

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個目

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

・4個目

# 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
このサイトを参考に解説してみたいと思います。
というか、読んだ方が早いかもですね。

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

for 変数 in データの集まり:
    処理

for文の流れは原則、「データの集まり」から、「データを一つずつ取り出す」という流れです

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

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

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

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

という感じになります。
いつもの通りに言うと、、

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

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

drange(0.,n+1,1.)

が、データの集まりだからです。これが何かというと、
3個目の関数になります。つまり下記の関数がデータの集まりをになっています。

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 の部分が成り立っている間は、下の部分の処理は実行してね!
という意味になります。

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

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

yield n
n += step

してね!という意味になります。

yieldとは?

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

forとyieldとwhile

今回のコードをみてみると

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

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

そうやってみていくと、

    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 = []

ここら辺までは大体同じ処理をしています。

                        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してくれます。こんな感じです。
スクリーンショット 2019-12-04 20.07.46.png

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

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

                        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)は何してるの?

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

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のとっかかりになてくれれば幸いです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした