| On this page |
関数とは、特定の演算を実行するコードブロックです。APEX Scriptでは、ユーザは独自の関数を作成したり、ビルトインのAPEX関数を使用することができます。
ユーザ定義関数 ¶
APEX Scriptのユーザ定義関数は、APEXネットワークビュー内でサブネットノードとして表現されます。サブネットノードの内容に関数ロジックが含まれます。
APEX Scriptコード:
# 関数定義
def test(a: Int, b: Int):
c: Int = a + b
return c
# 関数コール
x = test(1, 2)
Pythonと同様、ユーザはAPEX Scriptで独自の関数を定義することができます:
-
関数は、
defキーワードの後に関数名を使用することで定義します。 -
引数と呼ばれる情報は、括弧
()内のカンマで区切った値のリストを介して関数に渡すことができます。これらの引数は、関数名の後で指定します。 -
関数がコールされた時に実行されるコードブロックは、インデントする必要があります。
-
情報は関数によって返すことができ、それらの戻り値は
returnステートメントを使用して指定します。関数は、カンマで区切った複数の値を返すことができます。 -
関数は、コールされる前に定義しておく必要があります。
引数のない関数:
# 関数定義
def test():
a = 1.5
return a
# 関数コール; 結果はx = 1.5
x = test()関数コールの引数の数は、関数定義の引数の数と一致していなければなりません。関数定義の引数に注釈を付けることをお勧めします:
# 関数定義
def test(a: Int, b: Int):
c: Int = a + b
return c
# 関数コール
x = test(1, 2)キーワード引数 を使用して関数をコールすることができます。キーワード引数は、<key>=<value>構文の引数で、<key>は引数の名前です。キーワード引数を使用して関数をコールする場合には、引数の順序は問いません。
Note
キーワード引数と位置指定引数(非キーワード引数)の両方を使用して関数をコールする場合、キーワード引数は位置指定引数の後に配置する必要があります。
# 関数定義
def test(a: Int, b: Int):
c: Int = a + b
return c
# キーワード引数を使用した関数コール
x = test(b=1, a=2) __dot__ を含んだキーワード引数は'.'に置換されます。これにより、APEX Script はBase.skelやBase.shpなどのジオメトリ名を持つリグを処理することができます:
# 関数はBase.skelを受け取り、Base.shpを返します
def test(Base__dot__skel: Geometry):
Base__dot__shp = Geometry()
return Base__dot__shp
# グラフ入力はBase.skelです
geo = BindInput(Base__dot__skel=Geometry())
x: Geometry = test(geo)
関数は複数の値を返すことができ、引数リストの後の->に続いて戻り値の型を指定することができます。関数の戻り値の型は、丸括弧または大括弧を使って指定することができます:
def test(a: Vector3, b: Float) -> tuple[Vector3, Float]:
a = a * b
b = b + 1.0
return a, b
x, y = test(a=(3.0, 3.0, 3.0), b=5.5)関数の戻り値の型で、サブグラフまたはサブネットの出力ポートの名前を指定することができます。これを行なうには、 角括弧 を使用します。以下の例では、サブネットの出力ポート b の名前が outputy に変更されています:
def test(a: Vector3, b: Float) -> tuple[Vector3, Float[outputy]]:
a = a * b
b = b + 1.0
return a, b
x, y = test(a=(3.0, 3.0, 3.0), b=5.5)
BindOutput(x, y)
特定の戻り値を無視したい場合は、それらの値をアンダースコア(_)に割り当てます:
def test(a: Int):
a1: Int = a + 1
a2: Int = a + 2
a3: Int = a + 3
a4: Int = a + 4
return a1, a2, a3, a4
_, _, x, _ = test(1)関数の引数にデフォルト値を指定することができます。関数が引数なしでコールされた場合、デフォルト値が使用されます:
def foo(a: String = 'test', b: Int = Int(Float(1.5))):
return a, b
# 結果: x = 'test', y = 1
x, y = foo()関数コール内のエクスプレッション:
def foo(a: String, b: Int):
return a, b
m = 3
n = 4
p = 'test'
# 結果: x = 'test3', y = 7
x, y = foo(p+String(m), m+n)関数は、他の関数内にネスト化することができます:
def foo(a: Int):
return a+2
# この関数内でfooをコールします
def bar(a: Int) -> Int:
return foo(a)+4
# 結果: x1 = 3, x2 = 7
x1 = foo(1)
x2 = bar(1)関数は、数珠繋ぎにすることができます:
geo.computeTransform().transform(xform=Matrix4())
関数をサブグラフとして保存する ¶
APEX Script SOPを使用することで、関数をサブグラフとしてディスクに保存することができます。こうして保存されたサブグラフは、APEX Scriptコード内でグローバル関数として使用することができます。他のユーザが作成したものも含め、所有しているサブグラフはすべてAPEX Script内で関数として使用することができます。
関数をサブグラフとして保存するには:
-
APEX Script SOPで、 Subgraphs パラメータをオンにします。
-
Subgraphs セクションの Subgraph スニペットパラメータで関数を定義し、その関数定義の上に
@subgraphデコレータを記述します。例:
@subgraph def test(a: Int, b: Int): c: Int = a + b return c -
サブグラフは
@/apexgraph内に.bgeoファイルとして保存する必要があります。@はHOUDINI_PATHのディレクトリを展開します。ディレクトリを指定しなかった場合、サブグラフは$HOME/apexgraphに保存されます。サブグラフの場所とファイル名は、 Geometry File パラメータまたはサブグラフデコレータの引数の@subgraph(<file_path>)で設定することができます。 -
Save Subgraphs ボタンをクリックします。
同じAPEX Script SOP内または新規のAPEX Script SOP内でAPEX Scriptコードを記述する際に、その保存したサブグラフを関数として使用できるようになりました。
デコレータ ¶
デコレータとは、他の関数に対して追加処理を実施するものであり、@記号を付けて指定します。例:
@subgraph
@namespace('my_namespace')
@safeguard_inputs(True)
def test(a: Int, b: Int):
c: Int = a + b
return c利用可能なデコレータは以下の通りです:
サブグラフ ¶
@subgraphデコレータ使用すると、その直後に続く関数をサブグラフとして保存することができます。そのサブグラフはパックされたサブグラフとして保存されます - それはパックフォルダにプリミティブとして格納されます。
@subgraphデコレータはオプションの引数<file_path>を受け取り、またキーワード専用の引数の使用をサポートしています:
@subgraph([<file_path>], name=<name>, hidden=[True|False], ...)
引数 |
型 |
説明 |
|---|---|---|
name |
String |
サブグラフに割り当てられる名前。これは、パックされたサブグラフ上の |
hidden |
Bool |
Trueに設定されている場合、APEXネットワークビューのタブメニューにサブグラフは表示されません。この値は、グラフジオメトリ(アンパックサブグラフ)の |
また、カスタムのキーワード専用引数も指定することができ、これはグラフジオメトリのpropertiesアトリビュートに追加されます。
例:
以下の@subgraphデコレータは、サブグラフにtestnode::3.0という名前を付けます。nameキーワード引数がない場合、サブグラフはtestと名付けられます:
@subgraph('$HOME/houdini21.0/apexgraph/subgraph_test.bgeo',
name='testnode::3.0',
hidden=False,
extrainfo=123)
def test(a: Int, b: Int):
c: Int = a + b
return cサブグラフを保存した後は、以下の方法でそのサブグラフを使用することができます:
-
APEXネットワークビューでサブグラフ
testnodeを検索します:
-
APEX Scriptスニペットでサブグラフを関数として使用します:
s = testnode(1,2)
サブグラフのプロパティを確認するには、グラフをアンパックする必要があります:
-
File SOPを使用して、サブグラフを取り込みます:
-
Geometry File をサブグラフ
.bgeoファイルに設定します。この例では$HOME/houdini21.0/apexgraph/subgraph_test.bgeoです。 -
Geometry Spreadsheetで、上部のツールバーの
Primitivesをクリックします。サブグラフ名
testnode::3.0が、パックされたサブグラフの名前としてリストされます。
-
-
サブグラフはパックフォルダ内にあるため、そのプロパティを確認するには、
Unpack Folder SOPを使用してアンパックする必要があります:
-
Extract Pattern をサブグラフの名前
/testnode::3.0に設定します。 -
Geometry Spreadsheetで、上部のツールバーの
Detailをクリックします。サブグラフの
propertiesアトリビュートに以下の値がリストされます:{ "extrainfo": 123, "hidden": 0 }
-
ネームスペース ¶
@namespaceデコレータを使用すると、関数にネームスペースを指定することができます。関数をサブグラフとして保存した後、<namespace>.<function>を使用して関数をコールすることができます。@namespaceデコレータの構文は以下の通りです:
@namespace('<namespace>')
例えば、APEX Script SOPの Subgraphs セクションで次のサブグラフを定義することができます:
@subgraph
@namespace('my_namespace')
def test(a: Int, b: Int):
c: Int = a + b
return c関数をサブグラフとして保存した後、以下のステートメントを使用してサブグラフ関数をコールすることができます:
x = my_namespace.test(1,2)
入力の保護 ¶
@safeguard_inputsデコレータは、必要に応じて関数の入力引数のコピーを作成することで、グラフのソートの問題を防ぐことができます。@safeguard_inputsデコレータの構文は以下の通りです:
@safeguard_inputs(True|False)
@safeguard_inputsをTrueに設定すると、以下の条件の時に、APEX Scriptは関数の入力引数のコピーを作成します(グラフにValueノードを追加します):
-
入力引数がインプレースポートに渡されている場合。
且つ
-
その入力の名前が関数のどの出力名にも一致していない場合。
@safeguard_inputsをFalseに設定すると、APEX Scriptは関数の引数のコピーを作成しません。これによってグラフのソートの問題が生じる可能性があるため、@safeguard_inputsをFalseに設定する時は注意して使用してください。
@safeguard_inputsが関数の前にインクルードされていない場合、デフォルトでTrueに設定されます。
ビルトイン関数 ¶
APEX Script内では、ビルトイン関数のライブラリを利用することができます。これらの関数は、APEXネームスペース、または、オブジェクト指向の構文(この構文では、関数は入力の1つに作用します)のどちらかを使用してコールすることができます。使用可能なAPEX関数のリストは、APEXノードの索引、APEXネットワークビューでのTabメニュー、またはAPEX Script SOPか
APEX Autorig Component SOPの Snippet パラメータ内で文字入力した時の自動補完機能で確認することができます。
関数をコールするための様々な構文:
geo = BindInput(Geometry()) # APEXおよびSOPネームスペースを使用して関数をコールします a = apex.sop.copytopoints(geo) # 関数をコールするためのオブジェクト指向構文 b = geo.copytopoints()
以下の例では、APEXネームスペースを使用してappend()関数をコールしています。append()の1番目の入力はインプレースポートなので、入力配列
xは更新されます:
x = ['aaa'] x = apex.array.append_String(x, 'bbb') # 結果: x = ['aaa', 'bbb']
append()の結果を別の変数に割り当てた場合、元の配列は更新されません:
x = ['aaa'] y = apex.array.append_String(x, 'bbb') # 結果: x = ['aaa'], y = ['aaa', 'bbb']
オブジェクト指向の構文を使用する場合、関数の1番目の入力がその関数が作用する“オブジェクト”になっているので、その関数の1番目の入力を関数コールの前に記述します。このオブジェクトは、1番目の入力として関数に渡されます。この1番目の入力がインプレースポートだった場合、その入力はインプレース(直接データを書き換え)で更新されます:
x = ['aaa']
x.append_String('bbb')
# 結果: x = ['aaa', 'bbb']オブジェクト指向のappend()を変数に割り当てた場合、その変数にはappend()の2番目の戻り値が設定されます:
x = ['aaa']
idx = x.append_String('bbb')
# 結果: x = ['aaa', 'bbb'], idx = 1関数の1番目の入力がインプレースポートでなかった場合、その入力は更新されません:
x = ['aaa','bbb','ccc'] y = x.get_String(1) # 結果: x = ['aaa', 'bbb', 'ccc'], y = 'bbb'
引数ありで関数をコールする:
box = apex.sop.box(t=Vector3(1,1,1), scale=Float(2))
Sine()などのネームスペースなしの関数では、APEXネームスペースを前に付ける必要はありません:
# APEXネームスペースあり a = apex.sine(1.0) # APEXネームスペースなし b = sine(1.0)
デフォルトでは、APEX Scriptは最新バージョンのAPEX関数ではなく、コールされた通りのバージョンのAPEX関数を使用します。例えば、IntBitMaskToBool()は最初のバージョンの関数をコールします。2番目のバージョンのIntBitMaskToBoolを使用したい場合は、
IntBitMaskToBool.v2_0()をコールします。APEX関数の特定のバージョンを使用する方法については、特殊関数のHoudiniVersion()を参照してください。
テンプレート関数 ¶
テンプレート関数は、様々な型のデータを処理する関数です。例えば、geo::SetPrimAttribValue<T>はテンプレート関数で、<T>は様々な型のいずれかになります。テンプレート関数は、次の方法で指定することができます:
型を含む特定の関数名を使用する:
geo.setPrimAttribValue_String(prim, 'name', 'test')
valuetype引数を使用して型(valuetype=<type>)を指定する:
geo.setPrimAttribValue(prim, 'name', 'test', valuetype=String)
valuetype引数にリストまたはタプルを指定する:
b = Convert(a, valuetype=(Float, Int)) c = Convert(b, valuetype=[Int, Bool])
特殊関数 ¶
APEX Scriptには、グラフノードに情報を追加したり、特定のバージョンのAPEXノードを使用したり、グラフ内からグラフを呼び出す機能など、様々なグラフオペレーションを実行できる特殊関数が用意されています。
関数 |
説明 |
||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
BeginMetadata, EndMetadata |
ループおよびifブロックのブロック開始ノードと終了ノード(例えば y = 0
for x in range(3):
# ForBeginノードにメタデータを設定します
BeginMetadata(__name='test_begin', __color=(1,0,1), __tags=['tag1', 'tag2'])
y = y + 1
# ForEndノードにメタデータを設定します
EndMetadata(__name='test_end', __pos=(1,1,1), __tags=['out_tag'])
|
||||||||||||||
BindInput, BindOutput |
グラフの入力と出力を定義します。詳細は、BindInput()およびBindOutput()の使用方法についてはこの例を、BindInput()の使用例についてはグラフ入力を指定する方法を参照してください。 |
||||||||||||||
HoudiniVersion |
使用するAPEX関数のバージョンを設定します。例えば、 ヘッダテンプレート内にHoudiniバージョンを指定することもできます:
HoudiniVersion()関数では、
HoudiniVersion()に引数がない場合、これは |
||||||||||||||
InputMetadata, OutputMetadata |
サブネット内のグラフ入力(パラメータ)および出力ノードにメタデータを設定します。利用可能なメタデータは、特殊関数の引数に載っています。 def test(a,b):
# サブネット入力ノードにメタデータを設定します
InputMetadata(__name='abc', __pos=(1,0,0), __properties={'test': 1.5})
c: Float = a + b
# サブネット出力ノードにメタデータを設定します
OutputMetadata(__color=(1,0,1), __tags=['out_tag1','out_tag2'])
return c
x = test(1,2)
|
||||||||||||||
InvokeOutputs |
別のグラフを呼び出すを参照してください。 |
||||||||||||||
SetGraph |
APEXノードIDをグラフに関連付けるを参照してください。 |
||||||||||||||
StickyNote |
グラフにステッキーノートを作成します。特殊関数の引数に加え、以下に載せている引数を使用して、ステッキーノートに特定のプロパティを設定することができます:
例:
|
||||||||||||||
|
コピーを作成することなく、変数の別の識別子を作成します。これによって、グラフのソートの問題が発生する可能性があるため、注意して使用してください。 変数のエイリアスを作成します: x = 1.5 y = _portalias(x) # y = 1.5 BindOutput(y) 関数に割り当てられた変数のエイリアスを作成します: def test():
x = 1
return x
m = test()
m_alias = _portalias(m)
# m_alias = 1, n = 3
n = m_alias + 2
関数でエイリアスを使用します: def test(a: Int, b: Int):
c: Int = a + b
d = _portalias(c)
d += 2
return d
# m = 5
m = test(1, 2)
|
特殊関数の引数 ¶
以下に示す特殊関数の引数を使用すると、ノードに関する特定の情報を指定することができます:
引数 |
型 |
説明 |
|---|---|---|
|
|
ノードの名前。 |
|
|
ノードのカラー。 |
|
|
グラフ内のノードの位置。 |
|
|
ノードに格納されているプロパティ。 |
|
|
ノードに格納されているタグ。 |
例:
def abc(a: Vector3, b: Float):
a = a * b
b = b + 1.0
return a, b
# サブネットノード名は関数の名前に設定されます
subnet_a = abc(a=(3.0, 3.0, 3.0), b=5.5)
# サブネットノードの名前とカラーを設定するために使用される特殊関数の引数
subnet_b = abc(a=(3.0, 3.0, 3.0), b=5.5, __name='test_b', __color=(1,0,0))
関数のマッピング ¶
APEX Scriptでは、ビルトインのAPEX関数の一部に便利な名前のマッピングが用意されています。
グラフ関数 ¶
このセクションの例では、次の定義を使用します:
graph = ApexGraphHandle()
# グラフに2つのTransformObjectノードを追加します
a = graph.addNode('test_a', 'TransformObject')
b = graph.addNode('test_b', 'TransformObject')
# 辞書
d = {'xord': 3}関数 |
マップ先 |
例 |
|---|---|---|
|
graph::AddNode |
|
|
graph::ConnectInput |
|
|
graph::FindAndConnectInput |
|
|
graph::GetConnectedPorts |
|
|
graph::ConnectInput |
|
|
graph::FindAndConnectInput |
|
|
graph::Compile |
|
|
graph::FindOrAddPort |
|
|
Value<ApexGraphHandle> |
Valueノードにグラフを“保存”します。
|
|
graph::NodeInputs |
|
|
graph::NodeOutputs |
|
|
graph::FindPort |
|
|
graph::FindNodes |
|
|
graph::FindPorts |
|
|
graph::FindPorts |
|
|
graph::PromoteInput |
|
|
graph::PromoteOutput |
|
|
graph::UpdateNodeParms |
|
ノード関数 ¶
このセクションの例では、次の定義を使用します:
graph = ApexGraphHandle()
# TransformObjectノード'test_a'
a = graph.addNode('test_a', 'TransformObject')
# 辞書
d = {'abc': 3} 関数 |
マップ先 |
例 |
|---|---|---|
|
graphutils::NodeAncestors |
# ノードをサブネットにパックします
node_arr: ApexNodeIDArray = [a]
graph.packSubnet('my_test', node_arr)
# 'my_test'を返します
anc = a.ancestors()
|
|
graphutils::NodeCallbackName |
|
|
graph::NodeData |
|
|
graph::NodeInputs |
|
|
graph::NodeName |
|
|
graph::NodeOutputs |
|
|
graphutils::NodeParent |
|
|
graphutils::NodeParms |
|
|
graphutils::NodePath |
|
|
graph::FindPort |
|
|
graphutils::NodeProperties |
|
|
graph::UpdateNode |
|
|
graph::UpdateNode |
|
|
graph::UpdateNodeParms |
|
|
graph::UpdateNode |
|
|
graph::UpdateNodeProperties |
|
|
graph::UpdateNodeTags |
|
|
graphutils::NodeTags |
|
ポート関数 ¶
このセクションの例では、次の定義を使用します:
graph = ApexGraphHandle()
# グラフに2つのTransformObjectノードを追加します
a = graph.addNode('test_a', 'TransformObject')
b = graph.addNode('test_b', 'TransformObject')
# ノード'test_a'のポート
a_in = a.t_in
a_out = a.xform_out
# ノード'test_b'のポート
b_in = b.parent_in
b_out = b.t_out関数 |
マップ先 |
例 |
|---|---|---|
|
graph::ConnectInput |
グラフの同じネストレベルのポートを接続します。
|
|
graph::GetConnectedPorts |
|
|
graph::AddWirePath |
サブネットにあるノードに接続するなど、グラフの異なるネストレベルのポートを接続します。サブネットノードに作成される接続ポートの名前は、 # グラフにノードを追加します
c = graph.addNode('test_x', 'TransformObject')
# ノードをサブネットにパックします
node_arr: ApexNodeIDArray = [c]
graph.packSubnet('test_subnet', node_arr)
# サブネットのノードに接続します
a_out.connectPath(c.xform_in, 'abc')
|
|
graph::PortData |
|
|
graph::DisconnectPort |
|
|
graph::PortNode |
|
|
graph::PromotePort |
|
|
graph::GetPromotedPort |
|
|
graph::PromoteInput |
|
|
graph::PromoteOutput |
|
|
graph::GetSubPort |
subport()を使用してノードにサブポートを作成する場合、新規のサブポートに何かを接続するまで、グラフに表示されません。 # グラフにノードを追加します
c = graph.addNode('test_c', 'Add<Float>')
d = graph.addNode('test_d', 'Add<Float>')
# サブポート'dd'を作成します
d_in = d.b_in.subport('dd')
# サブポート'dd'に接続します
c.result.connect(d_in)
|
データ関数 ¶
このセクションの例では、次の定義を使用します:
# 辞書
d = {'abc': 3}
# ベクトル
v1 = (1,2,3)
v2 = (2,4,5)
# マトリックス
m = Matrix4(2)関数 |
マップ先 |
例 |
|---|---|---|
|
sop::kinefx::computetransform |
geo: Geometry = BindInput()
geo.addJoint('joint_1', Matrix4(2))
geo.addJoint('joint_2', Matrix4(3))
x = geo.computetransform(mode=2)
|
|
CrossProduct |
|
|
Distance |
|
|
Distance |
|
|
DotProduct |
|
|
Value<Dict>, Value<Geometry> |
Valueノードに辞書またはジオメトリを“保存”します。
|
|
Invert<Matrix4> |
この関数はインプレースで実行されます - 入力マトリックスが更新されるため、出力変数に割り当てる必要はありません。
|
|
transform::Dihedral<Matrix4> |
|
|
Normalize |
この関数はインプレースで実行されます - 入力ベクトルが更新されます:
normalize()が変数に割り当てられる場合、変数はベクトルの長さに設定されます:
|
|
Normalize |
|
|
transform::SmoothRotation |
|