| 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)特定の戻り値を無視したい場合は、それらの値をアンダースコア(_)に割り当てます:
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('$HIP/subgraph_test.bgeo')
|
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 |
|
ビルトイン関数 ¶
APEX Script内では、ビルトイン関数のライブラリを利用することができます。
これらの関数は、APEXネームスペース、または、オブジェクト指向の構文(この構文では、関数は入力の1つに作用します)のどちらかを使用してコールすることができます。
使用可能なAPEX関数のリストは、APEXノードの索引、APEXネットワークビューでのTabメニュー、またはAPEX Script SOP、APEX Autorig Component SOP、
APEX Rigscript 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>は様々な型のいずれかになります。 テンプレート関数は、次の2つの方法で指定することができます:
型を含む特定の関数名を使用する:
geo.setPrimAttribValue_String(prim, 'name', 'test')
valuetype引数を使用して型(valuetype=<type>)を指定する:
geo.setPrimAttribValue(prim, 'name', 'test', valuetype=String)
特殊関数 ¶
APEX Scriptには、グラフノードに情報を追加したり、特定のバージョンのAPEXノードを使用したり、グラフ内からグラフを呼び出す機能など、様々なグラフオペレーションを実行できる特殊関数が用意されています。
関数 |
説明 |
||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
BeginMetadata, EndMetadata |
ループおよびifブロックのブロック開始ノードと終了ノード(例えばForBeginやIfEnd)にメタデータを設定します。利用可能なメタデータは、特殊関数の引数に載っています。 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::ConnectInput |
|
|
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 |
この関数はインプレースで実行されます - 入力ベクトルが更新されます。
normalized()が変数に割り当てられる場合、変数はベクトルの長さに設定されます:
|
|
transform::SmoothRotation |
|
コンテンツライブラリのサンプル ¶
コンテンツライブラリのこのサンプルでは、象の鼻にサイン波のコントロールをセットアップする方法が説明されています: