ニッチなblender手記

世の中には自分に似た人が3人いるとされています。その人達へと情報共有するために主にblenderの記事を書いていきます。

loose introduction about Animation Nodes (Loop)

概要

下図を見て理解できるのであれば以降の項は読まなくていい。

f:id:r9aArrowhead:20201218151212j:plain

ここでは Animation Nodes が標準提供している Loop ノードについて調べる。

f:id:r9aArrowhead:20201218142309p:plain

参照

Animation Nodes - Doc - Loop

Wiki - iterator

Parameters And Iterators

ここの話は冒頭の画像が何しているか分かった上でなければ理解できない。

覚えておくべきは、ループ処理には以下のような要素が存在するということ。そしてこのページでは以下の要素に対する解説と利用方法が記載されているということ。

ヘッダ構成ミスか?

ここでループ処理に必須のインデックスやイテレータの話をしてくれているが、多分これを読んで分かる人はこの文章を読む必要がない。
Loop処理がビジュアルプログラミングの中でもプログラミング側に近い概念となるため仕方がないとは思う。

Dive into Python 3 - a-fibonacci-iterator のような話が理解できる前提の内容となっている。
つまり、インデックスもイテレータも知っている前提での話をしている。
よって割愛。

ここ読むならまずは上記リンクや 「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典 - for文 を読むぐらいの気概が必要となる。

f:id:r9aArrowhead:20201218142333p:plain

Generators

ここではジェネレータについての説明がある。
ジェネレータを理解するにはイテレータを知る必要がある。

f:id:r9aArrowhead:20201218142344p:plain f:id:r9aArrowhead:20201218142349p:plain f:id:r9aArrowhead:20201218142529p:plain f:id:r9aArrowhead:20201218142534p:plain f:id:r9aArrowhead:20201218142539p:plain f:id:r9aArrowhead:20201218142545p:plain f:id:r9aArrowhead:20201218142553p:plain

Advanced Node Settings

Advanced Node SettingsDescription という、ノードにラベルを追加できることだけ記載されているので割愛。

Iterator Sockets

ここではイテレータだがジェネレータで書いたことと重複したので割愛。

Parameter Sockets

  • Input:他ノードからのインプット可否の ON / OFF
  • Output:他ノードへのアウトプット可否の ON / OFF
  • Copy:後述の Reassignment Operators として利用できるようになる。別項があるのでここでは割愛。
  • Default:ソケットの初期値を設定する。パラメータとして選択したデータ型によってはなかったりする。
  • Hide:設定はあるが記述がない

f:id:r9aArrowhead:20201218142610p:plain f:id:r9aArrowhead:20201218142618p:plain f:id:r9aArrowhead:20201218142833p:plain

Reassignment Operators

ループ処理内で変更したデータを外部へ渡したい場合は、 Reassignする。これだけ覚えておけばいい。

基本的にリスト型のデータは immutable となっている。つまり、ループ処理中にパラメータで渡したリスト型データの中身を変えることはできない。

とはいえ、 Animation Nodes はリスト型であってもノードのネットワーク構成内容によってはリスト型の内容をコピーして mutable のように振る舞うため、Loop処理時ぐらいにしか気にすることはない。

f:id:r9aArrowhead:20201218142859p:plain f:id:r9aArrowhead:20201218142906p:plain f:id:r9aArrowhead:20201218142914p:plain f:id:r9aArrowhead:20201218142920p:plain

List Generators

ジェネレータで書いたことと重複したので割愛。

Break Condition

Loop処理の break を設定する。

f:id:r9aArrowhead:20201218142927p:plain f:id:r9aArrowhead:20201218142934p:plain f:id:r9aArrowhead:20201218142940p:plain

Execution Mechanism

これはループ処理の基本的なメカニズムが説明されている。

  1. Loop Input ノードはループ処理の開始として 現在 indexiterator が参照している番号を返す。そして、 Parameter として設定された値を返す。
  2. ループの本体(List Generators, Reassignment Operators, Break Condition) 、およびブレーク条件を除くすべてのLoop input ノードとネットワークを形成しているノードを、Loop Input ノード の出力ソケットが出力する値に基づいて実行される。
  3. break condition ノードの数だけ処理をする。
    1. Continue 入力ソケットの値が False の場合、ループを終了する。
  4. Generator Output ノードの数だけ処理する。
    1. Condition 入力ソケットの値が Trueの場合、ノードが参照しているリストへ値を追加する。
  5. Reassignment ノードの数だけ処理する。
    1. Condition 入力ソケットの値が True の場合、そのパラメータの値が入力値に変更されます。

上述の処理の流れを図にしたのが、冒頭に掲載したシーケンス図となる。以下に改めて再掲する。シーケンスを厳密にすると分かりにくくなるので、一番大きく処理が変わる breake 処理だけ分岐を記載した。

f:id:r9aArrowhead:20201218151212j:plain f:id:r9aArrowhead:20201218151221j:plain

examples

Animation Nodes のDocでは例をいくつかのせている。

挙動な内容についてはそれぞれページの画像を真似て作れば分かるので割愛。

loose introduction about Animation Nodes (Group)

概要

ここでは Anomation Nodes が標準で用意しているノードの内、 Subprograms に属する Group ノードを記載する。

f:id:r9aArrowhead:20201215150721p:plain

参照

Animation Nodes - Subprograms - Group

Group

一言で言えば、 Animation Nodes 版 NodeGroup Group は異なるノード同士をひとまとめにするノード。
プログラム的に言い換えれば、独自クラスを作成してその中に複数の変数や構造体を用意するノード。

特定のオブジェクトにだけ関わるノードを Group にまとめて、管理しやすくする。
また、入出力をまとめたり処理を中継することができるのでノードの整理がしやすくなる。

バニラの blender でいう ノードエディターのグループ化と同じような感覚で使える。

f:id:r9aArrowhead:20201215150727p:plain f:id:r9aArrowhead:20201215150833g:plain f:id:r9aArrowhead:20201215150733p:plain f:id:r9aArrowhead:20201215150742p:plain f:id:r9aArrowhead:20201215150747p:plain f:id:r9aArrowhead:20201215150754p:plain f:id:r9aArrowhead:20201215150800p:plain f:id:r9aArrowhead:20201215150807p:plain f:id:r9aArrowhead:20201215150814p:plain f:id:r9aArrowhead:20201215150820p:plain f:id:r9aArrowhead:20201215150827p:plain

loose introduction about Animation Nodes (DATA)

参照記事

Animation Nodes - Dosuments Data

Animation Nodes - Doc Loop

Reassignment や Copy 設定についてもちゃんと書いてくれている

Youtube - [Tut] advanced feature in the loop node P1; reassignment - blender animation nodes; AN-fundamental

基本原則

Animation Nodes はノードのソケットに色を付けることで、そのソケットがそのどんなデータを入力 / 出力するかを表現している。

主に利用されるdataタイプの場合、以下のような色分けがされている。

  • light blue : Integer data
  • blue : 3D Vector data
  • black : Object data
  • lavende : Float data
  • orange : Matrix data

基本原則として、同じ色同士のソケットでしかノードは接続できない。

f:id:r9aArrowhead:20201214171017p:plain

f:id:r9aArrowhead:20201214172059p:plain

Implicit Conversion

冒頭で ノードは同じデータ型でのみ接続できる と記載したが、一部例外が存在する。

Integer型の 1 や float型の 1.00 あるいは boolean型の True (1) は全てスカラー値としてみれば同じ 1 を表現している。
こうした 見え方は違うが中身は同じ データの型に関しては、多少のズレは発生することがあったとしても別のデータ型ソケットへ接続することができる。

1.01 を 1 に丸めた時に小数点以下の値が扱われなくなるのと同じ

ただし、 全てのデータ型が相互変換されるわけではない 点に注意。
この変換処理が行われるのは、以下のデータ型の間のみである。

  • Integer
    • 1, 2, 3 など整数値
  • Float
    • 1.2, 2.345 , 3.14159265359 など小数値
  • Boolean
    • True, False などのboolean値

f:id:r9aArrowhead:20201214171033p:plain

Dynamic Sockets

Dynamic Sockets は、Implicit Conversion に似た機能。 入力 / 出力ソケットのデータ型を、必要に応じて動的に自動的に変更する。

例えば、Get List Element ノードはリストを受け取ってその中から要素の1つを返すノードだが、出力ソケットを入力したデータ型にあわせた型に変更する。

これも全てのノードで型の変換をしてくれるわけではない

f:id:r9aArrowhead:20201214171040p:plain

Vectorized Sockets

Animation Nodes では1,2,3,4... というような複数のデータを持ったリストにある個々の要素に対して操作できるようになる Vectorize ( ベクトル化 ) という処理がある。

f:id:r9aArrowhead:20201214171047p:plain

Data Copying

ここではデータ型には mutableimutable の2種類があることが説明されるが、 Aimation Nodes 上でこの違いを判別することは難しい。

それは掲題の通り、本来なら mutable なオブジェクトに対して Data Copying という機能が働いてそうならないように処理しいているのが原因となる。

根っこが python なのでこの mutable immutable を理解したい場合はpythonの方で調べた方が分かりやすい。 むしろノードによるビジュアルプログラミングをするのであれば、中途半端に理解せず知らない状態のままの方が混乱しないかも

ここからは mutable についてそれなりに知っている、あるいはそもそも知らないことを前提に書いていく。

Animation Nodes では mutable なデータ型の場合でも、複数ノードから参照されている場合はノードの出力時に Data Copying という処理を行う。
つまり、 ユーザが意識しない限り、Animation Nodesで利用するデータ型は処理上では全て immutable という扱いになる。

設定で強制的に mutable に変更することは可能だが、ノードのようなビジュアルプログラムでそれをする必要は限られている。

f:id:r9aArrowhead:20201214171119p:plain

本家のドキュメントではここから mutableimmutable の例としてループ処理を取り上げているが、正直色々かっ飛ばして分かりにくい。
そのため、ノードの内容は変えずに手順を分解して記述する。

f:id:r9aArrowhead:20201214171125p:plain f:id:r9aArrowhead:20201214171534p:plain f:id:r9aArrowhead:20201214171540p:plain f:id:r9aArrowhead:20201214171547p:plain f:id:r9aArrowhead:20201214171554p:plain f:id:r9aArrowhead:20201214171601p:plain f:id:r9aArrowhead:20201214171607p:plain f:id:r9aArrowhead:20201214171613p:plain f:id:r9aArrowhead:20201214171621p:plain f:id:r9aArrowhead:20201214171627p:plain f:id:r9aArrowhead:20201214171635p:plain f:id:r9aArrowhead:20201214171645p:plain f:id:r9aArrowhead:20201214171652p:plain f:id:r9aArrowhead:20201214171658p:plain f:id:r9aArrowhead:20201214171704p:plain f:id:r9aArrowhead:20201214171710p:plain f:id:r9aArrowhead:20201214171719p:plain f:id:r9aArrowhead:20201214171725p:plain f:id:r9aArrowhead:20201214171732p:plain f:id:r9aArrowhead:20201214171739p:plain f:id:r9aArrowhead:20201214171747p:plain f:id:r9aArrowhead:20201214171754p:plain f:id:r9aArrowhead:20201214171802p:plain f:id:r9aArrowhead:20201214171809p:plain f:id:r9aArrowhead:20201214171817p:plain f:id:r9aArrowhead:20201214171825p:plain f:id:r9aArrowhead:20201214171831p:plain f:id:r9aArrowhead:20201214171838p:plain f:id:r9aArrowhead:20201214171849p:plain

Generic Data Type

データ型には Generic という色んなデータ型が入るものがある。
このデータ型を使うと、異なるデータ型をひとまとめにしたり、別のデータ型へ変換したりすることができる。

f:id:r9aArrowhead:20201214172044p:plain

Animation Nodes はある程度なら異なるデータ型でも自動で変換してくれるようになっているが、明示的にデータ型の変換を行いたい場合は、 Convert ノードを用いる。

ただし Convert ノードの変換にも限界はあるので注意。

f:id:r9aArrowhead:20201214172051p:plain

Scenes

f:id:r9aArrowhead:20201214172730p:plain

loose introduction about Animation Nodes (INTERFACE)

参照記事

Animation Nodes - Documentation Interface

Shortcuts

U

選択中のノードのプロパティ・シェルフ(Nキーで右側に展開されるサイドメニュー)に関するコンテキストメニューを表示する。

U (Advanced Node Settings and Socket Settings)

ノードを選択した状態で U キーを押下すると、Advanced Node Settings' と 'Socket Settings のメニューを表示する。

f:id:r9aArrowhead:20201211093305p:plain f:id:r9aArrowhead:20201211093312p:plain f:id:r9aArrowhead:20201211093319p:plain

W

選択中のノードの入出力ソケットに関する Pie Menu を表示する。

W (Data Input)

選択中のノードに入力ソケットがある場合は、 W キーを押下することで入力ソケットのデータ型に対応したデータ出力ノードを生成してくれる。

f:id:r9aArrowhead:20201211095144p:plain f:id:r9aArrowhead:20201211095150p:plain f:id:r9aArrowhead:20201211095156p:plain

W (Viewer)

Data Input の時とは逆の流れで、選択中のノードに出力ソケットがある場合は、 W キーを押下することで出力ソケットの中身を表示するための Viewer ノードを追加することができる。

f:id:r9aArrowhead:20201211095230p:plain f:id:r9aArrowhead:20201211095238p:plain f:id:r9aArrowhead:20201211095246p:plain f:id:r9aArrowhead:20201211095255p:plain

W (Loop Through)

出力ソケットのデータ型がイテレータな場合、 W キーを押下することで Loop Through というループ処理の中継ノードを追加することができる。

f:id:r9aArrowhead:20201211095418p:plain f:id:r9aArrowhead:20201211095426p:plain f:id:r9aArrowhead:20201211095431p:plain f:id:r9aArrowhead:20201211095439p:plain

W (Create Invoke Node)

ループ処理のインプットノードがある場合、 W キーを押下することで Loop Through と時と同じようにループ処理の中継ノードを追加することができる。

f:id:r9aArrowhead:20201211095502p:plain f:id:r9aArrowhead:20201211095508p:plain f:id:r9aArrowhead:20201211095515p:plain

E

選択中のノードを元にして、選択範囲を拡大 / 縮小するPie menu を表示してくれる。

E (Selected Dependencies)

f:id:r9aArrowhead:20201211100047p:plain f:id:r9aArrowhead:20201211100054p:plain

E (Selected Dependent)

f:id:r9aArrowhead:20201211100101p:plain f:id:r9aArrowhead:20201211100108p:plain

E (Selected Network)

f:id:r9aArrowhead:20201211100115p:plain f:id:r9aArrowhead:20201211100122p:plain

E (Frame Active Network)

f:id:r9aArrowhead:20201211100129p:plain f:id:r9aArrowhead:20201211100135p:plain

TAB

参照先のノードを選択してくれる。どれ参照しているのか分からなくなった時とかに便利そう。

f:id:r9aArrowhead:20201211100202p:plain f:id:r9aArrowhead:20201211100209p:plain

loose read about Sverchok documents (Introduction Unit 03)

Lesson 03 - A Grid

ここではグリッド状の平面オブジェクトを作成していくことで、パラメトリックデザインと動的トポロジーの概念を学習する。

グリッドは、一般的な幾何学的プリミティブであり、x軸とy軸で細分化された平面と考えることができる。

あるいは小さな板ポリを格子状に並べたオブジェクト

Sverchok の平面ジェネレータはグリッドを作成し、関連する辺とポリゴンを含む。
ここでは、 Sverchok に標準で用意されている Plane ノードを使うのではなく、ノードツリーを作成してグリッドを作成する。 これがどのように動作するかを理解すれば、要点を理解したことになり、より複雑なオブジェクトへの応用ができる。 パラメトリックデザインと動的トポロジーの重要な概念をいくつか取り上げます。

元記事

Introduction to modular components - Lesson 03 - A Grid

What do we know about Grids?

ここからは下図のようにx軸とy軸が細分化された平面を作成していく。

参照画像

Where to start?

ここからはビューポート上に図示しながらの解説が進んでいく。

レッスンとしてグリッドが選ばれたのは、グリッドが以下のような単純なプロパティしかもたないためである。

  • X Side Length
  • Y Side Length
  • X num subdivs
  • Y num subdivs

上記のプロパティに関しては表示する手段があるらしいが、ここではその手段は記載されずに図でのみ解説されている。

参照画像

Decide which variables you want to expose

作成しようとしているグリッドの内、以下のプロパティから作成していく。

  • Side X total length
  • Side Y total length
  • Number Vertices on side X
  • Number Vertices on side Y

Think in Whole numbers (integers, ints) if you can

グリッドを作成するために、形状を数学的に単純化して取り組んでいく。
下図は座標を説明するためにXY軸上に描かれたグリッドである。
今回はXY平面上にグリッドを作成するのでZ軸は無視できるが、完全性のためにZ軸も含んでいる。

参照画像

上図で 4 * 3 頂点を選んだ理由は、それがグリッドを解説する上での最小の形の一つであり、任意の次元のグリッドに変換するためには微調整するだけいいから。
3 * 3や4 * 4を選ばない理由は、xとyに異なる頂点数を使用することでX軸が4でY軸が3であることの関係を見出しやすいから。

上図の4 * 3 グリッドの各頂点の第一成分のシーケンスを考える。

[0,1,2,3,0,1,2,3,0,1,2,3]

続いて第二成分のシーケンス。

[0,0,0,0,1,1,1,1,2,2,2,2]

これらは Sverchok では簡単にノードで生成できる。

Lesson 01 や Lesson 02 で実際にやった、 Number Range などの組み合わせによる頂点の生成のこと。

Using modulo and integer division to get grid coordinates

ここでは、周期的な関数 (periodic functions) や段階的な増加 (stepwise increases、インクリメント) の話をするときに常に関わってくる2つの数学的な概念(操作)を取り上げる。

  • modulo, or the symbol %
  • integer division, or the symbol //

module および integer division 共に Sverchok には標準機能として Scala Math ノードの設定として搭載されている

Operands

Lesson 01 と 02 で Scalar Matth ノード利用したが、Scalar Matth ノード にはオペランドと呼ばれる多くの操作がある。ここでは、それらを用いて頂点の構成要素を得ていく。

operand Symbol Behaviour
Modulo (mod) % i % 4 returns the division remainder of i / 4 , rounded down to the nearest whole number
Integer Division // i // 4 returns the result of i / 4 , rounded down to the nearest whole number.

1つ目は剰余算を計算するためのオペランド、 下は除算した結果を整数値に丸めるためのオペランド

これらのオペランドを用いると、下記のような計算をすればそれぞれ第一成分と第二成分のシーケンスと同じ結果になる:

  • i % 4 to turn [0,1,2,3,4,5,6,7,8,9,10,11] into [0,1,2,3,0,1,2,3,0,1,2,3]
  • i // 4 to turn [0,1,2,3,4,5,6,7,8,9,10,11] into [0,0,0,0,1,1,1,1,2,2,2,2]

数値に対して moduloint.division を利用した時の結果テーブルは下記の通り

number range (n) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
n % 4 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2
n // 4 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3
n % 6 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2
n // 6 0 0 0 0 0 0 1 1 1 1 1 1 2 2 2

下図は上のテーブルに繰り返しやシーケンスの流れが分かりやすいようにマーカーをつけたものとなっている。

参照画像

このレッスンでは Sverchok によるビジュアルプログラミンを学ぶことが目的となっている。
上記の数学的な処理や概念といった角度から、基礎的な数学的な知識や概念をコード化していくことになる。

グリッドの頂点を計算するために %// を使用する2つのコード (python) を後述する。このコードは、必要な頂点の数(12 = 4 * 3)に基づいて、頂点のxとyの成分を生成するアルゴリズムになっている。
どちらも結果は変わらない。

あえて比較するのであれば、 python を利用する以上は連想配列で処理する後者の方が処理速度は速くなる

# the for-loop version:

num_verts_x = 4
num_verts_y = 3
j = num_verts_x * num_verts_y      # the * symbol means multiplication

final_list = []
for i in range(j):                 # passes: 0 1 2 3 4 5 6 7 8 9 10 11
   x = i % 4                       #  makes: 0 1 2 3 0 1 2 3 0 1 2 3
   y = i // 4                      #  makes: 0 0 0 0 1 1 1 1 2 2 2 2
   final_list.append((x, y, 0))
# the list comprehension version:

num_verts_x = 4
num_verts_y = 3
j = num_verts_x * num_verts_y      # the * symbol means multiplication

final_list = [(i % 4, i // 4, 0) for i in range(j)]

上記2つのコードは、どちらも同じ結果を出力する。

[(0, 0, 0), (1, 0, 0), (2, 0, 0), (3, 0, 0),
 (0, 1, 0), (1, 1, 0), (2, 1, 0), (3, 1, 0),
 (0, 2, 0), (1, 2, 0), (2, 2, 0), (3, 2, 0)]

これら頂点は、最終的に下図のような形となる。

参照画像

ビジュアルプログラミングはタイピングが少ないことを除けば、従来のプログラミングと非常に似ている。 ノードをクリックしてドラッグして情報の流れを作っても、テキストエディタでコードを書いても、アルゴリズムは同じとなる。

Making vertices

下図を用いれば、グリッド状に配置する頂点を生成することができる。

  • Vector In
  • Sclar Math (3x) notice I minimized the Multiplication Node.
  • A Number (2x)
  • Number Range (int)

y=3 に x=4 を掛け合わせて 12 となり、これが頂点の数となる。
Number Range ノードへ接続した頂点の数が [0,1...11] の範囲を決定している。

参照画像

ノードを用いたパラメトリックな構成ができたので、X と Y の頂点を設定しているノードの値を変えるとグリッドの結果が変わることも確認できるようになっている。

補足として、全てのノードの計算結果も表示した内容が下図となる。

f:id:r9aArrowhead:20201209145007p:plain

Making Polygons

ここまででグリッド状のメッシュを作成するために必要な頂点グループを作成することが可能となった。
ここからは作成した頂点グループを利用してポリゴンを作成していく。

どこからポリゴンを作成するかについては色々あると思うが、ここでは例として頂点インデックスの若い順にポリゴンを作成していくようになっている。詳細は本家の図を参照。

参照画像

ポリゴンを作成していくに辺り、いくつかの注意点が存在する。
上図の例にあるような頂点グループの場合、単純に4つの頂点をグループ化してポリゴンを作成していくというやり方は採用できない。

そうした地続きを前提としたポリゴンの生成をした場合、下図のように意図していないポリゴンが頂点インデックス [3,7,8,4] で作成されてしまう。

参照画像

上図のようなことにならないよう、ポリゴンを作成するために利用する頂点インデックスの組み合わせは下記の通り。

#    |A   B   C   D|
#    ---------------
0  = [0,  4,  5,  1]
1  = [1,  5,  6,  2]
2  = [2,  6,  7,  3]
#  = [3,  7,  8,  4] .. not a valid polygon for x=4, y=3 ここがあるとさっきのような意図しないポリゴンが発生する
3  = [4,  8,  9,  5]
4  = [5,  9,  10, 6]
5  = [6,  10, 11, 7]

組み合わせがわかれば、後はこれをマジックナンバーを用いずに作成するために数式へと置き換える必要がある。

今回のケースにおける観点は以下の通り。

  • BはAより4つ多い
  • CはBより1つ多い
  • DはAより1つ多い

A(第一成分)を基準にすると上記のような規則性を見出すことができた。後はこれをノードやプログラムで式として表現していけばいい。

Aに該当する頂点インデックスを与えると、ポリゴンの作成に必要なB, C, Dの頂点インデックスを計算する式

まずは blenderpython を用いずに数式として表現する。

A = A
B = (A + 4) # BはAより4つ多い
C = (A + 4 + 1) # CはBより1つ多い
D = (A + 1) DはAより1つ多い

# 4の部分は固定なので、固定値は別の変数にできそう

A = A
B = (A + offset)
C = (A + offset + 1)
D = (A + 1)

まずは上記パターンをプログラムとして表現する。

[A, (A + offset), (A + offset + 1), (A + 1)]

ここまででポリゴンを貼るための式は大体できたが、試作した式だけでは例の連続していない頂点インデックスの間でポリゴンが張られてしまう問題を回避できていない。

[3,7,8,4] の頂点インデックスを参照してしまう問題のこと

この問題は今回に限った話ではなく、4 * 4 などの一直線に頂点が並んでいないグリッドでれば必ず発生する。

#    |A   B   C   D|
#    ---------------
0  = [0,  4,  5,  1]   # row 1  column 1
1  = [1,  5,  6,  2]   # row 1  column 2
2  = [2,  6,  7,  3]   # row 1  column 3
#  = [3,  7,  8,  4] .. not a valid polygon for x=4, y=4
3  = [4,  8,  9,  5]   # row 2  column 1
4  = [5,  9,  10, 6]   # row 2  column 2
5  = [6,  10, 11, 7]   # row 2  column 3
#  = [7,  11, 12, 8] .. not a valid polygon for x=4, y=4
6  = [8,  12, 13, 9]   # row 3  column 1
7  = [9,  13, 14, 10]  # row 3  column 2
8  = [10, 14, 15, 11]  # row 3  column 3

A polygon Algorithm

ノードでくみ上げるとこうなるということが図示されているだけで、具体的な構築方法は記載なし。

参照画像

参照画像

私家版 A polygon Algorithm 補足

ハンズオンできなかったので、勝手に解析した手順。

f:id:r9aArrowhead:20201209145020p:plain

ここでのレッスンではグリッド状のポリゴンを作成することが目的になっているが、基本的には以下の手順でくみ上げていく。

  • 作成するグリッド状ポリゴンの頂点を作成(これはこのページで記載されているので割愛)
  • 作成した頂点から作成する面の列数と行数を計算する
  • ポリゴンを貼るための頂点インデックスの組み合わせを作成する
  • 表示する

ポリゴンの列数と行数を計算する

頂点グループは既に 3 * 4 のグリッド状に作成しているが、面のグループはまだ作成されていない。
グリッドの行列の数も改めて生成しておく必要がある。

f:id:r9aArrowhead:20201209145029p:plain f:id:r9aArrowhead:20201209145038p:plain

作成する面の列数と行数を計算する

f:id:r9aArrowhead:20201209145046p:plain f:id:r9aArrowhead:20201209145055p:plain f:id:r9aArrowhead:20201209145104p:plain

頂点インデックスの組み合わせを作成する

f:id:r9aArrowhead:20201209145113p:plain f:id:r9aArrowhead:20201209145121p:plain f:id:r9aArrowhead:20201209145127p:plain f:id:r9aArrowhead:20201209145134p:plain f:id:r9aArrowhead:20201209145143p:plain

表示する

f:id:r9aArrowhead:20201209145151p:plain

loose read about Sverchok documents (Introduction Unit 02)

Lesson 02 - A Circle

概要

このレッスンでは、以下のノードを利用して円を作成する。

  • List Length
  • List Shift
  • List Zip
  • Formula
  • Lesson 01で既に利用しているノードたち

A Circle

最も手っ取り早い方法は、 blender 標準の circle オブジェクトやカーブを用いる方法がある。
次に簡単な方法は、 Sverchok が標準で提供している Circle Generator ノードを用いること。

ここでは、上記を用いずに正方形メッシュを作成した時と同じように頂点位置の計算や設定を全てノードを使って生成する方法で説明している。

Dynamic Polygons

正方形を作成した時のようにすれば頂点さえ作成できればポリゴンを生成することができる。
しかし、頂点数が増えると頂点の管理やインデックスの把握が困難になるので、複雑な頂点情報は変更が発生した時の修正をある程度自動化しなければならない。

Sverchok では UV Connect ノードというものがそれに該当するが、本チュートリアルではこのノードも未使用ということになっている。

利用しない理由は、基本的なノードの使い方やVisual Programmingでの位置づけを学ぶために頂点やインデックスの生成を自分で構築させるのも本チュートリアルで目指しているとのこと。

Generating an index list for the polygon

自動的に頂点のリストを作成するためには、処理の瞬間に何個の頂点があるのかを知る必要がある。
List Length を使うと、リストがネストしていてもネストの深さに合わせた閲覧ができる。

Add -> List Main -> List Length

ここからは [0,1,2,...n]のようなシーケンス(リスト)を生成していく。Pythonとして書く場合には以下のようなコードになる。

n = 4
range(start=0, end=n, step=1)
>> [0, 1, 2, 3]

まずは Lesson.1 で頂点インデックスを手動で指定していたが、ここを Number Range ノードだけで処理していくようにする。

f:id:r9aArrowhead:20201203142150p:plain f:id:r9aArrowhead:20201203142157p:plain

ここまでのノード画面を見ると、 List Lengthn=4 に相当する出力を作成したことが分かる。
n=4 に該当するノードができたのであれば、次は range(start=0, end=n, step=1) に相当するノードが必要となる。 そのためのノードが Number Range ということになる。

Add -> Numbers -> Number Range

List Length -> Number Range とする。

f:id:r9aArrowhead:20201203142207p:plain

Number Rangestep モードは pythonrange とほぼ同じ名前のソケットがあるので、画面のように range(start=0, end=n, step=1) 対応するような設定をすれば、 [0, 1, 2, 3] という結果が得られた。

ここまでの処理で頂点インデックスを自動生成するところまでは実現できたが、このインデックスをそのまま Viewer Draw へ接続してもポリゴンは生成してくれない。
その理由は、 Number Range の出力結果がblender がポリゴンとして認識できる形となっていないためである。
この問題を修正するために、 Add -> Numbers -> Fomula ノードを追加してデータの形を修正する。

f:id:r9aArrowhead:20201203142214p:plain f:id:r9aArrowhead:20201203142222p:plain f:id:r9aArrowhead:20201203142229p:plain f:id:r9aArrowhead:20201203142236p:plain

Number RangeFomula を経由する前と後のデータの形と、 正方形ポリゴンを出力するために利用している Simple Topology の出力しているデータの形を比較すると、違いが分かりやすい。

ここまでできれば頂点インデックスの形が正方形ポリゴンの出力に使っているものと同じになるので、 Viewer Draw に接続すればポリゴンを生成してくれるようになる。

f:id:r9aArrowhead:20201203142244p:plain

Generating the set of circular verts

ここではあくまで円形のポリゴンを作成することが目的なので、いよいよ頂点データを円形のものへと差し替えていく。
今使っている頂点データは正方形を描画するために Number Range を使って生成したものとなっている。
円形(のように見えるポリゴン)の場合は正方形よりも多くの頂点データが必要となるので、 ノードの先頭にある Number Range の設定を変更して 円形になるように単位円辺りのポリゴンの数を増やせばいい。

f:id:r9aArrowhead:20201203142252p:plain

Forcing an even spread of Vertices

頂点インデックスをノードだけで生成できるようになったので、今度は頂点の数を指定すれば円に必要な頂点もノードだけで計算してくれるようにする。

今は Number Range を使って頂点の数がどれだけ必要かを考えてから円を作っている状態。

そのためには、 一番最初に作成した Number RangeStep で指定している数値を計算で算出する必要がある。
頂点の数に合わせた円周上の頂点の間隔を計算するには、以下のような式となる。

step_distance = 2.0 / n # n は円を表現するために利用する頂点の数

まずは step_distance = 2.0 / n の式を表現するノードを追加する。

Add -> Numbers -> Scalar Math

f:id:r9aArrowhead:20201203142300p:plain f:id:r9aArrowhead:20201203142308p:plain

次に、利用する頂点の数を表現するノードを追加する。

'Add -> Numbers -> A Number'

f:id:r9aArrowhead:20201203142315p:plain

後は n を表現する Numberstep_disance を算出するための Scalar Math を接続すれば計算は完了。
Scalar MathNumber Range -> Step へとつなげば、直接ステップ数を指定しないで円を表現できるようになる。

f:id:r9aArrowhead:20201203142322p:plain

円はできたが、元々表示していた円よりも頂点の数が多いという現象が発生している。
これは Number Range で設定している StopStep へ接続した 計算結果によるもの。
Step = 0.1 となっている状態なので Start (=0.0) から Stop (=2.0) まで 0.1刻みで数値を生成している 状態となっている。

接続する前は Step = 2.0 だった

ただし、単純に Stop = 1.0 にして出力される値の数を半減させると下図のようになる。

f:id:r9aArrowhead:20201203142330p:plain

この解決方法として、 Sverchok のドキュメントでは tau (タウ) による計算へと切り替えていた。

f:id:r9aArrowhead:20201203142339p:plain f:id:r9aArrowhead:20201203142346p:plain

別解。

f:id:r9aArrowhead:20201203142353p:plain

Polygon was easy, what about Edges?

ここまでで円形のポリゴンを作成してきたが、ここからは円形のエッジを作成していく。

円形の頂点を用いてエッジを生成する場合、エッジの生成に必要な頂点インデックスの指定方法は以下のようになる。

[[0,1],[1,2],[2,3],[3,0]].

# n個の頂点を用いた円形のエッジを生成するために必要なインデックスはこんな感じ
[[0,1],[1,2],...,[n-1,n],[n,0]]

# 上記のリストを n 個の頂点が収まったリストだけで頂点インデックスを生成する場合は、for文を使えばよい
n = 5
for i in range(n):
    print(i, (i+1) % n)

>>> 0 1
>>> 1 2
>>> 2 3
>>> 3 4
>>> 4 0

# 頂点インデックスを sverchok がエッジの組み合わせと認識できるような形へ整形する必要がある
n = 5
edges = [[i, (i+1) % n] for i in range(n)]
print(edges)

>>> [[0, 1], [1, 2], [2, 3], [3, 4], [4, 0]]

# 上の頂点インデックスの組み合わせを見ると、基本的に 元の配列と中身が1つズレている配列の組み合わせということが分かる
# そのため、pythonのzipメソッドを用いれば、for文を簡略化できる

indices                = [0, 1, 2, 3, 4]
indices_shifted_by_one = [1, 2, 3, 4, 0] # indicesの中身を1つずらした

for a, b in zip(indices, indices_shifted_by_one):
    print([a, b])

>>> [0, 1]
>>> [1, 2]
>>> [2, 3]
>>> [3, 4]
>>> [4, 0]

pythonを知らなければ上の式は意味不明に見えるが、 for a, b in zip(indices, indices_shifted_by_one): の部分を Sverchook で表現しようということになる。

難しいこと言っているように見えるが、今回のケースに限っては Sverchok にそのまんまのノードがあるためそれらを利用する。
利用するのは以下の2つのノード。

add -> List Struct -> List Shift

add -> List Main -> List Zip

f:id:r9aArrowhead:20201203142401p:plain f:id:r9aArrowhead:20201203142408p:plain f:id:r9aArrowhead:20201203142415p:plain f:id:r9aArrowhead:20201203142423p:plain f:id:r9aArrowhead:20201203142431p:plain f:id:r9aArrowhead:20201203142440p:plain f:id:r9aArrowhead:20201203142449p:plain f:id:r9aArrowhead:20201203142455p:plain

参考

Sverchok - Lesson 02 - A Circle

loose read about Sverchok documents (Introduction Unit 01)

Unit 01 - Introduction to modular components

prerequisites

ここからの記述は基本的なベクトルと三角法を理解している必要がある。
もし Sverchok を最大限に活用したいと思っていても、数学のバックグラウンドを持っていないのであれば、まずは数学の知識を重ねるか別のアプローチで利用する必要がある。

作者は KhanAcademy のコンテンツをここで提示している。 ただし英語。

Lesson 01 - A Plane

このレッスンで扱うノードは以下の通り。

  • Scalar Math
  • Vector In
  • Number
    • グループ名?、ノード名は A Number
  • Number Range
  • Viewer Draw
  • Stethoschope
  • Simple Topology
  • Vector Math

全て Sverchok のノード

これらのノードを用いて Sverchok のみでblender上へ平面を作成する。
4つのベクトルが必要となるが、それらを数学で定義します。 単位円 (unit-circle) の三角関数を使って、0.5PIの座標を求める。

本家の説明画像リンク

単位円と内包される正方形 (Plane) については以下の通り。


円の外周:{\displaystyle C={2}\pi{r}}=360^\circ \\
単位円の半径:1 \\
外周: {\displaystyle C={2}\pi} \\
内包される正方形の頂点の間隔:\frac{{2}\pi}{4}=0.5\pi=90^\circ

上記のような考えからの場合、最終的には 0, 90, 180, 270 の角度に作れば Plane は作成できる。
しかしその場合は各頂点がXY軸線上に乗っかった形となるので、ビューポート上ではひし形の Plane として生成される。 デフォルトのBlender が作成する Plane オブジェクトのようにするためには45º回転させる必要があるため、始点となる頂点は0*PI=0ºではなく、0.25PI=45ºになる。

そのためには、 Sverchok のノード上でも [0.25π, 0.75π, 1.25π, 1.75π] の頂点群となるように表現する必要がある。
この頂点群の範囲指定には Number Range ノードを用いる。

数学的な説明を書ききるつもりはないので、ざっくりメモ。 ここに関しては 働きアリ the 2nd - math 算数のコツ(18) 円の中の正方形 とか見た方が早い

大雑把な処理の流れ

  1. 頂点を算出
  2. 頂点情報を作成
  3. 頂点を描画
  4. エッジを描画
  5. 面を描画

Making a series of numbers

Add -> Number -> Number Range

f:id:r9aArrowhead:20201127133301p:plain f:id:r9aArrowhead:20201127133307p:plain

デフォルトでは、このノードは 0 - 10 までの数値を 1.0 刻みで生成する。
より正確には 0.0 から 1.0 刻みで数値を 10 個生成する

[0.0, 1.0, 2.0, 3.0.....9.0] 9.0で止まっている点に注意。これは日本語的には「0以上10未満」なので、10は含まれない。 31日までとか言うと31日も含めるから厄介だよね・・・ ちなみにこの挙動は float と int の違いはあれど、 python の range(0, 10, 1) と同じ 内部処理が python だからこうなっているんだろうなと推測 Python Doc - func range Python Doc - typesseq-range

Seeing the output of the Range Float node

Add -> Text -> Stethoscope

f:id:r9aArrowhead:20201127133313p:plain f:id:r9aArrowhead:20201127133318p:plain

あるいは、さきほど追加した Number Range ノードを Ctrl + 右クリック 選択することでも Stethoscope ノードを追加できる。

Sverchok - Stethoscope

Number Range -> Stethoscope とノードをつなぐと、ツリービュー上に Number Range が生成した数値を表示する。
もし数値が見えにくい場合は、 Stethoscope ノードのカラーピッカーで表示色を変更できる。

Stethoscope の表示内容に小数点以下の情報がないが、データ上はちゃんと保持している。 このノードは小数点表示をする際、整数に丸めても問題ない場合(=小数点以下全て 0)ならば整数で表示する。

Setting up the input values of Number Range to generate the 4 multipliers

Number Range ノードに単位円に内包される正方形が持つ頂点位置の情報を設定する

  1. Set the Number Range mode to Step
    • Number Range ノードの modestep へ変更する
  2. make sure the Start value is 0.25
    • 'Start' の値を 0.25 に設定する
  3. Step value is 0.50
    • Step の値を 0.50 に設定する
  4. Set the Count slider to 4 Count の値を 4にする

f:id:r9aArrowhead:20201127133325p:plain

Multiplying the Range by PI

Add -> Number -> Scalar Math

Scalar Math には 円周率計算用のプロパティ (pi * x) があるので、これを Number Range の出力に対して追加する。

f:id:r9aArrowhead:20201127133329p:plain f:id:r9aArrowhead:20201127133337p:plain f:id:r9aArrowhead:20201127133100p:plain f:id:r9aArrowhead:20201127133105p:plain

これによって、2つのノードを経由した値は [0.25, 0.75, 1.25, 1.75] * pi すなわち [0.25 * pi, 0.75 * pi, 1.25 * pi, 1.75 * pi] となる。

Getting the Sine and Cosine of this range

Add -> Number -> Scalar Math

三角関数の計算用に Scalar Math ノードを更に追加して、さっき作成した円周率計算用ノードの output と接続する。
ノードを追加した後で、計算方式を Sin & Cos へと変更する。
Sin & Cos のノードは output が2つあるので、それぞれに Stethoscope を接続して計算結果を出力させると Sin と Cos のそれぞれの計算をしてくれているのが分かる。

f:id:r9aArrowhead:20201127133110p:plain f:id:r9aArrowhead:20201127133116p:plain f:id:r9aArrowhead:20201127133121p:plain

Making Vectors from a range of numbers

Add -> Vector -> Vector In

Vector In ノードで Sin & Cos の出力結果をベクトル値へと変換する。

  1. Cos ( x ) を Vector in の最初の入力ソケットへ接続する。(Xへ接続)
  2. Sin ( x ) を Vector in の 2 番目の入力ソケットへ接続する。 (Yへ接続)
  3. Vector In の3番目のソケット (Z) は空にする。

Vector In ノードは入力ソケットが接続されていない場合、対応する値を 0.0 として扱う。 今回の利用方法だと Z = 0.0 となる

f:id:r9aArrowhead:20201127133126p:plain f:id:r9aArrowhead:20201127133131p:plain

なぜ Scala Math ノードの出力を交差させているかについては割愛。 高校数学の美しい物語 - 三角関数の基本公式

Display Geometry

Add -> Viz -> Viewer Draw

前項の値を見ると、既に [x, y, z] という3D座標に対応した値になったことが分かる。
Viewer Draw ノードを追加すると、ようやく算出した座標を 3Dビューポート上に頂点として出力することができるようになった。

プログラムのように頂点の内部データを見る場合は Stethoscope 、 3Dビュー上で確認したい(=視覚化したい)場合は Viewer Draw を使う必要があることがこれで分かった。

f:id:r9aArrowhead:20201127133137p:plain f:id:r9aArrowhead:20201127133142p:plain

Increasing the Size of the Vertex

Viewer Node ノードにはビューポート上に表示している頂点のサイズや色を変更する機能が備わっている。

f:id:r9aArrowhead:20201127133147p:plain f:id:r9aArrowhead:20201127133152p:plain f:id:r9aArrowhead:20201127133200p:plain

Make some edges

頂点を作成することができたので、エッジを描画していく。
頂点が4つあるので頂点を示すインデックスも [0,1,2,3] の4つが存在する。
この頂点インデックスを [[0,1],[1,2],[2,3],[3,0]] という風に繋いで4つのエッジを生成する。

alt+space でノード検索メニューを表示して top と入力すると、 Topology Simple ノードというノードがヒットするので、それを追加。
追加したノードには EdgeFace というにプロパティがあるので、 Edge の方に以下のコード(頂点インデックスを指定したエッジのトポロジ)を入力する。

# input
# 頂点インデックスの組み合わせを指定している
0 1, 1 2, 2 3, 3 0          <--- easy to input as a human

# produces
# python標準の書き方はこっちだが、これをそのまま入力するとエラーのなるので注意
[[0,1],[1,2],[2,3],[3,0]]   <--- let python worry about the list syntax

f:id:r9aArrowhead:20201127133205p:plain f:id:r9aArrowhead:20201127133210p:plain f:id:r9aArrowhead:20201127133216p:plain

入力が終わった Toporology SimpleViewer Node -> Edges へ接続する。
そうすると、ノードの Vertices で指定した頂点群を利用して、指定したインデックス同士の繋がりをもったエッジがビューポート上に表示される。

f:id:r9aArrowhead:20201127133221p:plain

Make a first Polygon

Topology Simple ノードには Face のプロパティもあることから、面を貼る場合にも利用できる。
ここでは、さっき作ったノードで同時に面も貼る。

Topology Simple -> Face へ 面を貼るために利用する頂点インデックスを指定する。
今回は4つのインデックスがあるので 0 1 2 3 と指定。

そしてViewer Node -> Polygons と接続すれば、ビューポート上に面が表示されるようになった。

ワインディングとかは気にしなくてもいいのかな?

f:id:r9aArrowhead:20201127133227p:plain

Controlling the size of the Polygon

頂点のスケールを変更する方法は複数あり、ここでは Vector Math ノードを用いたスケールアップ方法を記述している。

まず、 Vector -> Vector Math でノードを追加する。
そして、 Vector Math の計算方法を指定するプロパティを Multiple Scalar へと変更。
スカラ値の乗算を処理するようにしたところで、頂点データへ変換している Vector In ノードと接続する。

f:id:r9aArrowhead:20201127133233p:plain f:id:r9aArrowhead:20201127133238p:plain f:id:r9aArrowhead:20201127133242p:plain

Multiple Scalar はデフォルト値が 1 なので、これだけでは頂点データに特に変化はない。
乗算する値を Vector Math のソケットへ直接指定する方法もあるが、プロシージャルということで乗算に用いる値もノードで用意しよう。
そのため、 A Number -> Number ノードを追加してプロパティに値を入力、 Vector Math の2番目の入力ソケットと接続すればビューポート上で Number で指定した値の分だけスケールが変化する。

f:id:r9aArrowhead:20201127133249p:plain f:id:r9aArrowhead:20201127133255p:plain

参考

Sverchok - Introduction to modular components