PolyDataの記述:線分¶
VTKにおいて線分情報をPolyDataを用いて記述してみる. データセット要素 PolyData のうち,PointsとLinesを用いて幾何形状を形成し,Schalar Data をのせる. 例として,
で表される螺旋を描く.
PolyData (Line) の例¶
PolyDataデータセット要素を用いて3次元空間内で線分を表現できる. PolyDataを用いた記述例を以下に示す.
<?xml version="1.0" encoding="utf-8"?>
<VTKFile type="PolyData">
<PolyData>
<Piece NumberOfPoints="21" NumberOfLines="20" NumberOfVerts="0" NumberOfStrips="0" NumberOfPolys="0">
<PointData Scalars="Line">
<DataArray Name="Line" type="Float64" NumberOfComponents="1" format="ascii">
0.0 5.0 10.0 15.0 20.0 25.0 30.0 35.0 40.0 45.0 50.0 55.0 60.0 65.0 70.0 75.0 80.0 85.0 90.0 95.0 100.0
</DataArray>
</PointData>
<Points>
<DataArray Name="points" type="Float64" NumberOfComponents="3" format="ascii">
1.0 0.0 0.0
1.0 -1.2246467991473533e-15 0.3090169943749474
1.0 -2.4492935982947065e-15 0.5877852522924731
1.0 -1.0779367755043061e-14 0.8090169943749473
1.0 -4.898587196589413e-15 0.9510565162951535
1.0 9.82193361864236e-16 1.0
1.0 -2.1558735510086122e-14 0.9510565162951536
1.0 -1.5677954951632475e-14 0.8090169943749475
1.0 -9.797174393178826e-15 0.5877852522924732
1.0 -3.9163938347251765e-15 0.3090169943749475
1.0 1.964386723728472e-15 1.2246467991473532e-16
1.0 7.84516728218212e-15 -0.30901699437494773
1.0 -4.3117471020172244e-14 -0.587785252292473
1.0 -3.7236690461718594e-14 -0.8090169943749473
1.0 -3.135590990326495e-14 -0.9510565162951535
1.0 -2.54751293448113e-14 -1.0
1.0 -1.9594348786357652e-14 -0.9510565162951536
1.0 -1.3713568227904002e-14 -0.8090169943749476
1.0 -7.832787669450353e-15 -0.5877852522924732
1.0 -1.952007110996705e-15 -0.3090169943749476
1.0 3.928773447456944e-15 -2.4492935982947064e-16
</DataArray>
</Points>
<Lines>
<DataArray Name="connectivity" type="Int64" NumberOfComponents="1" format="ascii">
0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 17 17 18 18 19 19 20
</DataArray>
<DataArray Name="offsets" type="Int64" NumberOfComponents="1" format="ascii">
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40
</DataArray>
</Lines>
</Piece>
</PolyData>
</VTKFile>
線分データの記述様式¶
線分をPolyDataを用いて書く場合,<Points> と <Lines> タグを用いて,線分を表現する. <PolyData>タグ内( <PolyData> <Piece> ... </Piece> </PolyData> )の構成要素は,
<PointData>... </PointData> ( 線分にのせるスカラー情報 : [nData] )
<Points>...</Points> ( 線分を構成する 点群 : [nData,NumberOfComponents=3] )
<Lines>...</Lines> ( 線分の 接続情報 . connectivity / offsets により構成 )
<DataArray Name="connectivity" >...</DataArray> ( 各データ間の 連結 (0,1), (1,2), ... )
<DataArray Name="offsets" >...</DataArray> ( 各構成要素データの 終点 を示す 2 4 6 8 ... )
PolyData(Line) 作成用クラス¶
PolyDataを用いて,線分を記述するためのクラス, vtk_makePolyData_line を以下に示す.
入出力¶
vtk_makePolyData_line( vtkFile=None, Data=None, xyz=None, VectorData=False, DataFormat="ascii" )
vtkFile = ( default : out.vtp )
Data = [ NoPoints, NoLines ]
xyz = [ NoPoints, NoCoords(3), NoLines ]
VectorData = True or False ( default : False )
DataFormat = ( default : "ascii" )
コード¶
import sys, subprocess
import numpy as np
# ========================================================= #
# === vtk_makePolyData_line class === #
# ========================================================= #
class vtk_makePolyData_line():
# ------------------------------------------------- #
# --- class Initiator --- #
# ------------------------------------------------- #
def __init__( self, vtkFile=None, Data=None, xyz=None, \
VectorData=False, DataFormat="ascii", ):
# --- [1-1] Arguments --- #
if ( vtkFile is None ): vtkFile = "out.vtp"
# --- [1-2] Variables Settings --- #
self.vtkFile = vtkFile
self.vtkContents = ''
self.vtkEndTags = ''
self.xyz = xyz
self.Data = Data
self.DataFormat = DataFormat
self.VectorData = VectorData
self.DataDims = None
self.NoLines = None
self.NoPoints = None
self.NoCoords = None
# --- [1-3] Routines --- #
self.inquireLineData( xyz=self.xyz, Data=self.Data, VectorData=self.VectorData )
self.vtk_add_VTKFileTag( datatype="PolyData" )
self.vtk_add_PolyDataTag_Line( Data=self.Data, xyz=self.xyz, VectorData=self.VectorData )
self.vtk_writeFile()
# ------------------------------------------------- #
# --- vtk_add_VTKFileTag --- #
# ------------------------------------------------- #
def vtk_add_VTKFileTag( self, datatype=None ):
# ------------------------------------------------- #
# --- [1] Add XML Definition & VTKFile Tag --- #
# ------------------------------------------------- #
if ( datatype is None ): datatype = "ImageData"
self.vtkContents += '<?xml version="1.0"?>\n'
self.vtkContents += '<VTKFile type="{0}">\n'.format( datatype )
self.vtkEndTags = '</VTKFile>' + '\n' + self.vtkEndTags
# ------------------------------------------------- #
# --- vtk_add_PolyDataTag --- #
# ------------------------------------------------- #
def vtk_add_PolyDataTag_Line( self, xyz=None, Data=None, VectorData=None, DataName="Line", \
NoPoints=None, NoSegments=None, NoVerts=0, NoStrips=0, NoPolys=0 ):
# ------------------------------------------------- #
# --- [1] Arguments --- #
# ------------------------------------------------- #
if ( xyz is None ): xyz = self.xyz
if ( Data is None ): Data = self.Data
if ( VectorData is None ): VectorData = self.VectorData
if ( NoPoints is None ): NoPoints = self.NoPoints
if ( NoSegments is None ): NoSegments = self.NoPoints-1
self.inquireLineData( xyz=xyz, Data=Data, VectorData=VectorData )
connect, offsets = self.prepareLineInfo( NoPoints=NoPoints )
# ------------------------------------------------- #
# --- [2] Open PolyData Tag --- #
# ------------------------------------------------- #
self.vtkContents += '<PolyData>\n'
self.vtkContents += '<Piece NumberOfPoints="{0}" NumberOfLines="{1}" ' \
'NumberOfVerts="{2}" NumberOfStrips="{3}" NumberOfPolys="{4}">\n'\
.format( NoPoints, NoSegments, NoVerts, NoStrips, NoPolys )
# ------------------------------------------------- #
# --- [3] add Point & Line Data --- #
# ------------------------------------------------- #
# -- [3-1] Data -- #
self.vtkContents += '<PointData {0}="{1}">\n'.format( "Scalars", DataName )
self.vtkContents += self.vtk_add_DataArray( Data=Data, DataName=DataName, VectorData=False )
self.vtkContents += '</PointData>\n'
# -- [3-2] xyz points -- #
self.vtkContents += '<Points>\n'
self.vtkContents += self.vtk_add_DataArray( Data=xyz, DataName="points", VectorData=True )
self.vtkContents += '</Points>\n'
# -- [3-3] connectivity -- #
self.vtkContents += '<Lines>\n'
self.vtkContents += self.vtk_add_DataArray( Data=connect, DataName="connectivity", VectorData=False )
self.vtkContents += self.vtk_add_DataArray( Data=offsets, DataName="offsets" , VectorData=False )
self.vtkContents += '</Lines>\n'
# ------------------------------------------------- #
# --- [4] Close PolyData Tags --- #
# ------------------------------------------------- #
self.vtkContents += '</Piece>\n'
self.vtkContents += '</PolyData>\n'
# ------------------------------------------------- #
# --- vtk_add_DataArray --- #
# ------------------------------------------------- #
def vtk_add_DataArray( self, Data=None, DataName=None, DataFormat=None, DataType=None, nComponents=None, nData=None, VectorData=False ):
if ( Data is None ): sys.exit( "[vtk_add_DataArray -@makeStructuredGrid-] Data == ??? " )
if ( DataName is None ): sys.exit( "[vtk_add_DataArray -@makeStructuredGrid-] DataName == ??? " )
if ( DataFormat is None ): DataFormat = self.DataFormat
if ( DataType is None ): DataType = self.inquiryData( Data=Data, ret_DataType =True, VectorData=VectorData )
if ( nComponents is None ): nComponents = self.inquiryData( Data=Data, ret_nComponents=True, VectorData=VectorData )
if ( nData is None ): nData = self.inquiryData( Data=Data, ret_nData =True, VectorData=VectorData )
ret = ""
ret += '<DataArray Name="{0}" type="{1}" NumberOfComponents="{2}" format="{3}">\n'\
.format( DataName, DataType, nComponents, DataFormat )
lines = ""
if ( nComponents == 1 ):
for line in np.ravel( Data ):
lines += "{0} ".format( line )
lines += "\n"
else:
for line in Data:
lines += ( " ".join( [ str( val ) for val in line ] ) + "\n" )
ret += lines
ret += '</DataArray>\n'
return( ret )
# ------------------------------------------------- #
# --- vtk_writeFile --- #
# ------------------------------------------------- #
def vtk_writeFile( self, vtkFile=None ):
if ( vtkFile is None ): vtkFile = self.vtkFile
with open( vtkFile, "w" ) as f:
f.write( self.vtkContents )
f.write( self.vtkEndTags )
subprocess.call( ( "xmllint --format --encode utf-8 {0} -o {0}"\
.format( vtkFile ) ).split() )
print( "[vtk_writeFile-@makePolyData_line-] VTK File output :: {0}".format( vtkFile ) )
# ------------------------------------------------- #
# --- inquiryData --- #
# ------------------------------------------------- #
def inquiryData( self, Data=None, VectorData=False, ret_DataType=False, ret_nComponents=False, ret_nData=False ):
if ( Data is None ): sys.exit( "[inquiryData-@vtk_makeImageData-] Data == ??? " )
# ------------------------------------------------- #
# --- [1] DataType Check --- #
# ------------------------------------------------- #
if ( type(Data) is not np.ndarray ):
sys.exit( "[inquiryData-@vtk_makeImageData-] Data should be np.ndarray [ERROR]" )
if ( Data.dtype == np.int32 ): DataType = "Int32"
if ( Data.dtype == np.int64 ): DataType = "Int64"
if ( Data.dtype == np.float32 ): DataType = "Float32"
if ( Data.dtype == np.float64 ): DataType = "Float64"
# ------------------------------------------------- #
# --- [2] Data Shape Check --- #
# ------------------------------------------------- #
if ( VectorData is True ):
nComponents = Data.shape[-1]
nData = np.size( Data[-1][:] )
else:
nComponents = 1
nData = np.size( Data[:] )
# ------------------------------------------------- #
# --- [3] Return --- #
# ------------------------------------------------- #
if ( ret_DataType ): return( DataType )
if ( ret_nComponents ): return( nComponents )
if ( ret_nData ): return( nData )
return( { "DataType":DataType, "nComponents":nComponents, "nData":nData } )
# ------------------------------------------------- #
# --- vtk_inquireLineData --- #
# ------------------------------------------------- #
def inquireLineData( self, xyz=None, Data=None, VectorData=False ):
# ------------------------------------------------- #
# --- [1] Arguments --- #
# ------------------------------------------------- #
if ( xyz is None ): xyz = self.xyz
if ( xyz is None ): return( None )
if ( Data is None ): Data = self.Data
if ( type( xyz ) is not np.ndarray ):
print( "[inquireLineData-@makePolyData_Line-] xyz should be np.ndarray [ERROR]" )
if ( xyz.ndim >= 5 ):
sys.exit( "[inquireLineData-@makePolyData_Line-] incorrect xyz size ( ndim >= 5 ) [ERROR]" )
# ------------------------------------------------- #
# --- [2] DataDims & LILJLK Check --- #
# ------------------------------------------------- #
if ( VectorData is True ):
self.NoPoints = xyz.shape[0]
self.NoCoords = xyz.shape[1]
self.NoLines = xyz.shape[2]
else:
self.NoPoints = xyz.shape[0]
self.NoCoords = xyz.shape[1]
self.NoLines = 1
xyz = xyz.reshape( xyz.shape + (1,) )
# ------------------------------------------------- #
# --- [3] prepare Data --- #
# ------------------------------------------------- #
if ( Data is None ): Data = np.zeros( ( self.NoPoints, self.NoLines ) )
return( xyz, Data )
# ------------------------------------------------- #
# --- prepareLineInfo --- #
# ------------------------------------------------- #
def prepareLineInfo( self, NoPoints=None ):
if ( NoPoints is None ): NoPoints = self.NoPoints
connect = np.ravel( np.array( [ [ ik, ik+1 ] for ik in range( NoPoints-1 ) ] ) )
offsets = np.ravel( np.array( [ 2*(ik+1) for ik in range( NoPoints-1 ) ] ) )
return( connect, offsets )
# ======================================== #
# === 実行部 === #
# ======================================== #
if ( __name__=="__main__" ):
import myUtils.genArgs as gar
args = gar.genArgs()
tAxis = np.linspace( 0.0, 100.0, 10001 )
xAxis = np.cos( 2.*np.pi*tAxis )
yAxis = np.sin( 2.*np.pi*tAxis )
zAxis = np.sin( 2.*np.pi*tAxis * 0.01 )
import myConvert.pilearr as pil
xyz = np.transpose( pil.pilearr( (xAxis,yAxis,zAxis), axis=1 ) )
Data = np.copy( tAxis )
vtk = vtk_makePolyData_line( xyz=xyz, Data=Data )