PyVistaの拡張#

pyvista.PolyData のような pyvista.DataSet は,ユーザが拡張することができます. 例えば,ユーザーがメッシュ上の(1, 0, 1)方向の最大点の位置を記録したいとします.

ユーザーがサブクラス化を処理する方法は2つあります. 1つは,タイプ・オブジェクトを直接管理する方法です. この場合,フィルター操作時に型のチェックが必要になることがあります.

2つ目は,タイプの自動管理です. ユーザーは,特定のタイプのDataSetsに対して,ユーザー定義のクラスをほぼ常に使用するかどうかを制御することができます.

注釈

これは上級者向けの機能です. 型の自動管理は,すべての状況で機能するわけではありません.特に,組み込みデータセットが直接インスタンス化されている場合はそうです. 以下の例をご覧ください.

import numpy as np
import vtk

import pyvista

pyvista.set_plot_theme("document")

pyvista.PolyData のユーザー定義サブクラスである FooData が定義されています.これには,メッシュ上で(1, 0, 1)方向に最も離れた点を追跡するプロパティが含まれています.

class FooData(pyvista.PolyData):
    @property
    def max_point(self):
        """Returns index of point that is furthest along (1, 0, 1) direction."""
        return np.argmax(np.dot(self.points, (1.0, 0.0, 1.0)))

タイプを直接管理する#

これで FooData 型の foo_sphere オブジェクトが作成されました.ポイントのインデックスと位置を直接取得することができます.球体の半径は0.5なので,(1, 0, 1)の方向への最大の広がりは \(0.5\sqrt{0.5}\approx0.354\) となります.

foo_sphere = FooData(pyvista.Sphere(theta_resolution=100, phi_resolution=100))
print("Original foo sphere:")
print(f"Type: {type(foo_sphere)}")
print(f"Maximum point index: {foo_sphere.max_point}")
print(f"Location of maximum point: {foo_sphere.points[foo_sphere.max_point, :]}")
Original foo sphere:
Type: <class '__main__.FooData'>
Maximum point index: 26
Location of maximum point: [0.35634708 0.         0.35073745]

pyvista.DataSet.rotate_y() のようなインプレース操作を使っても,オブジェクトの型には影響しません.

foo_sphere.rotate_y(90, inplace=True)
print("\nRotated foo sphere:")
print(f"Type: {type(foo_sphere)}")
print(f"Maximum point index: {foo_sphere.max_point}")
print(f"Location of maximum point: {foo_sphere.points[foo_sphere.max_point, :]}")
Rotated foo sphere:
Type: <class '__main__.FooData'>
Maximum point index: 4926
Location of maximum point: [ 3.5073745e-01 -1.1460996e-16  3.5634708e-01]

しかし,フィルター操作によって,元の型とは異なるものを含む,異なる DataSet 型が返されることがあります. このような場合, decimate メソッドは pyvista.PolyData オブジェクトを取得します.

print("\nDecimated foo sphere:")
decimated_foo_sphere = foo_sphere.decimate(0.5)
print(f"Type: {type(decimated_foo_sphere)}")
Decimated foo sphere:
Type: <class 'pyvista.core.pointset.PolyData'>

オブジェクトを明示的に FooData にラップすることが必要になりました.

decimated_foo_sphere = FooData(foo_sphere.decimate(0.5))
print(f"Type: {type(decimated_foo_sphere)}")
print(f"Maximum point index: {decimated_foo_sphere.max_point}")
print(f"Location of maximum point: {foo_sphere.points[foo_sphere.max_point, :]}")
Type: <class '__main__.FooData'>
Maximum point index: 2481
Location of maximum point: [ 3.5073745e-01 -1.1460996e-16  3.5634708e-01]

タイプを自動的に管理する#

デフォルトの pyvista.DataSet タイプは, pyvista._wrappers を使って設定することができます.一般的には,ユーザー定義のクラスを主に使用することが予想される場合に,このメソッドを使用するのがベストです.

この例では,これまで pyvista.PolyData として作成されていたすべてのオブジェクトが, FooData オブジェクトとして作成されます.キーとなるのは,基礎となるvtkオブジェクトであることに注意してください.

pyvista._wrappers['vtkPolyData'] = FooData

FooData オブジェクトを取得するために, pyvista.PolyData オブジェクトを特別にラップする必要がなくなりました.

foo_sphere = pyvista.Sphere(theta_resolution=100, phi_resolution=100)
print("Original foo sphere:")
print(f"Type: {type(foo_sphere)}")
print(f"Maximum point index: {foo_sphere.max_point}")
print(f"Location of maximum point: {foo_sphere.points[foo_sphere.max_point, :]}")
Original foo sphere:
Type: <class '__main__.FooData'>
Maximum point index: 26
Location of maximum point: [0.35634708 0.         0.35073745]

rotate_y のようなインプレース操作を使っても,オブジェクトの型には影響しません.

foo_sphere.rotate_y(90, inplace=True)
print("\nRotated foo sphere:")
print(f"Type: {type(foo_sphere)}")
print(f"Maximum point index: {foo_sphere.max_point}")
print(f"Location of maximum point: {foo_sphere.points[foo_sphere.max_point, :]}")
Rotated foo sphere:
Type: <class '__main__.FooData'>
Maximum point index: 4926
Location of maximum point: [ 3.5073745e-01 -1.1460996e-16  3.5634708e-01]

pyvista.PolyData を返していたフィルター操作が FooData を返すようになりました.

print("\nDecimated foo sphere:")
decimated_foo_sphere = foo_sphere.decimate(0.5)
print(f"Type: {type(decimated_foo_sphere)}")
print(f"Maximum point index: {decimated_foo_sphere.max_point}")
print(f"Location of maximum point: {foo_sphere.points[foo_sphere.max_point, :]}")
Decimated foo sphere:
Type: <class '__main__.FooData'>
Maximum point index: 2481
Location of maximum point: [ 3.5073745e-01 -1.1460996e-16  3.5634708e-01]

ユーザーは,ネイティブの pyvista.PolyData オブジェクトを作成することができますが,この方法を使用すると,意図しない結果になる可能性があります. このような場合は,型を直接管理する方法を使用することをお勧めします.

poly_object = pyvista.PolyData(vtk.vtkPolyData())
print(f"Type: {type(poly_object)}")
# catch error
try:
    poly_object.rotate_y(90, inplace=True)
except TypeError:
    print("This operation fails")
Type: <class 'pyvista.core.pointset.PolyData'>
This operation fails

pyvista._wrappers を使用する際には,使用されないケースに設定が漏れないように,デフォルト値をリセットする必要があるかもしれません.

pyvista._wrappers['vtkPolyData'] = pyvista.PolyData

局所的な使い方が望ましい場合には,ティアダウン方式を推奨します. 例えば, try...finally ブロックです.

try:
    pyvista._wrappers['vtkPolyData'] = FooData
    # some operation that sometimes raises an error
finally:
    pyvista._wrappers['vtkPolyData'] = pyvista.PolyData

Total running time of the script: (0 minutes 0.151 seconds)

Sphinx-Galleryによるギャラリー