init
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
"""Utility modules for the VTK-Python wrappers."""
|
||||
|
||||
__all__ = ['colors', 'misc', 'vtkConstants', 'vtkImageExportToArray',
|
||||
'vtkImageImportFromArray', 'vtkMethodParser', 'vtkVariant',
|
||||
'numpy_support', 'pickle_support']
|
||||
@@ -0,0 +1,216 @@
|
||||
# This module defines many standard colors that should be useful.
|
||||
# These colors should be exactly the same as the ones defined in
|
||||
# vtkNamedColors.h.
|
||||
|
||||
# Whites
|
||||
antique_white = (0.9804, 0.9216, 0.8431)
|
||||
azure = (0.9412, 1.0000, 1.0000)
|
||||
bisque = (1.0000, 0.8941, 0.7686)
|
||||
blanched_almond = (1.0000, 0.9216, 0.8039)
|
||||
cornsilk = (1.0000, 0.9725, 0.8627)
|
||||
eggshell = (0.9900, 0.9000, 0.7900)
|
||||
floral_white = (1.0000, 0.9804, 0.9412)
|
||||
gainsboro = (0.8627, 0.8627, 0.8627)
|
||||
ghost_white = (0.9725, 0.9725, 1.0000)
|
||||
honeydew = (0.9412, 1.0000, 0.9412)
|
||||
ivory = (1.0000, 1.0000, 0.9412)
|
||||
lavender = (0.9020, 0.9020, 0.9804)
|
||||
lavender_blush = (1.0000, 0.9412, 0.9608)
|
||||
lemon_chiffon = (1.0000, 0.9804, 0.8039)
|
||||
linen = (0.9804, 0.9412, 0.9020)
|
||||
mint_cream = (0.9608, 1.0000, 0.9804)
|
||||
misty_rose = (1.0000, 0.8941, 0.8824)
|
||||
moccasin = (1.0000, 0.8941, 0.7098)
|
||||
navajo_white = (1.0000, 0.8706, 0.6784)
|
||||
old_lace = (0.9922, 0.9608, 0.9020)
|
||||
papaya_whip = (1.0000, 0.9373, 0.8353)
|
||||
peach_puff = (1.0000, 0.8549, 0.7255)
|
||||
seashell = (1.0000, 0.9608, 0.9333)
|
||||
snow = (1.0000, 0.9804, 0.9804)
|
||||
thistle = (0.8471, 0.7490, 0.8471)
|
||||
titanium_white = (0.9900, 1.0000, 0.9400)
|
||||
wheat = (0.9608, 0.8706, 0.7020)
|
||||
white = (1.0000, 1.0000, 1.0000)
|
||||
white_smoke = (0.9608, 0.9608, 0.9608)
|
||||
zinc_white = (0.9900, 0.9700, 1.0000)
|
||||
|
||||
# Greys
|
||||
cold_grey = (0.5000, 0.5400, 0.5300)
|
||||
dim_grey = (0.4118, 0.4118, 0.4118)
|
||||
grey = (0.7529, 0.7529, 0.7529)
|
||||
light_grey = (0.8275, 0.8275, 0.8275)
|
||||
slate_grey = (0.4392, 0.5020, 0.5647)
|
||||
slate_grey_dark = (0.1843, 0.3098, 0.3098)
|
||||
slate_grey_light = (0.4667, 0.5333, 0.6000)
|
||||
warm_grey = (0.5000, 0.5000, 0.4100)
|
||||
|
||||
# Blacks
|
||||
black = (0.0000, 0.0000, 0.0000)
|
||||
ivory_black = (0.1600, 0.1400, 0.1300)
|
||||
lamp_black = (0.1800, 0.2800, 0.2300)
|
||||
|
||||
# Reds
|
||||
alizarin_crimson = (0.8900, 0.1500, 0.2100)
|
||||
brick = (0.6100, 0.4000, 0.1200)
|
||||
cadmium_red_deep = (0.8900, 0.0900, 0.0500)
|
||||
coral = (1.0000, 0.4980, 0.3137)
|
||||
coral_light = (0.9412, 0.5020, 0.5020)
|
||||
deep_pink = (1.0000, 0.0784, 0.5765)
|
||||
english_red = (0.8300, 0.2400, 0.1000)
|
||||
firebrick = (0.6980, 0.1333, 0.1333)
|
||||
geranium_lake = (0.8900, 0.0700, 0.1900)
|
||||
hot_pink = (1.0000, 0.4118, 0.7059)
|
||||
indian_red = (0.6900, 0.0900, 0.1200)
|
||||
light_salmon = (1.0000, 0.6275, 0.4784)
|
||||
madder_lake_deep = (0.8900, 0.1800, 0.1900)
|
||||
maroon = (0.6902, 0.1882, 0.3765)
|
||||
pink = (1.0000, 0.7529, 0.7961)
|
||||
pink_light = (1.0000, 0.7137, 0.7569)
|
||||
raspberry = (0.5300, 0.1500, 0.3400)
|
||||
red = (1.0000, 0.0000, 0.0000)
|
||||
rose_madder = (0.8900, 0.2100, 0.2200)
|
||||
salmon = (0.9804, 0.5020, 0.4471)
|
||||
tomato = (1.0000, 0.3882, 0.2784)
|
||||
venetian_red = (0.8300, 0.1000, 0.1200)
|
||||
|
||||
# Browns
|
||||
beige = (0.6400, 0.5800, 0.5000)
|
||||
brown = (0.5000, 0.1647, 0.1647)
|
||||
brown_madder = (0.8600, 0.1600, 0.1600)
|
||||
brown_ochre = (0.5300, 0.2600, 0.1200)
|
||||
burlywood = (0.8706, 0.7216, 0.5294)
|
||||
burnt_sienna = (0.5400, 0.2100, 0.0600)
|
||||
burnt_umber = (0.5400, 0.2000, 0.1400)
|
||||
chocolate = (0.8235, 0.4118, 0.1176)
|
||||
deep_ochre = (0.4500, 0.2400, 0.1000)
|
||||
flesh = (1.0000, 0.4900, 0.2500)
|
||||
flesh_ochre = (1.0000, 0.3400, 0.1300)
|
||||
gold_ochre = (0.7800, 0.4700, 0.1500)
|
||||
greenish_umber = (1.0000, 0.2400, 0.0500)
|
||||
khaki = (0.9412, 0.9020, 0.5490)
|
||||
khaki_dark = (0.7412, 0.7176, 0.4196)
|
||||
light_beige = (0.9608, 0.9608, 0.8627)
|
||||
peru = (0.8039, 0.5216, 0.2471)
|
||||
rosy_brown = (0.7373, 0.5608, 0.5608)
|
||||
raw_sienna = (0.7800, 0.3800, 0.0800)
|
||||
raw_umber = (0.4500, 0.2900, 0.0700)
|
||||
sepia = (0.3700, 0.1500, 0.0700)
|
||||
sienna = (0.6275, 0.3216, 0.1765)
|
||||
saddle_brown = (0.5451, 0.2706, 0.0745)
|
||||
sandy_brown = (0.9569, 0.6431, 0.3765)
|
||||
tan = (0.8235, 0.7059, 0.5490)
|
||||
van_dyke_brown = (0.3700, 0.1500, 0.0200)
|
||||
|
||||
# Oranges
|
||||
cadmium_orange = (1.0000, 0.3800, 0.0100)
|
||||
cadmium_red_light = (1.0000, 0.0100, 0.0500)
|
||||
carrot = (0.9300, 0.5700, 0.1300)
|
||||
dark_orange = (1.0000, 0.5490, 0.0000)
|
||||
mars_orange = (0.5900, 0.2700, 0.0800)
|
||||
mars_yellow = (0.8900, 0.4400, 0.1000)
|
||||
orange = (1.0000, 0.5000, 0.0000)
|
||||
orange_red = (1.0000, 0.2706, 0.0000)
|
||||
yellow_ochre = (0.8900, 0.5100, 0.0900)
|
||||
|
||||
# Yellows
|
||||
aureoline_yellow = (1.0000, 0.6600, 0.1400)
|
||||
banana = (0.8900, 0.8100, 0.3400)
|
||||
cadmium_lemon = (1.0000, 0.8900, 0.0100)
|
||||
cadmium_yellow = (1.0000, 0.6000, 0.0700)
|
||||
cadmium_yellow_light = (1.0000, 0.6900, 0.0600)
|
||||
gold = (1.0000, 0.8431, 0.0000)
|
||||
goldenrod = (0.8549, 0.6471, 0.1255)
|
||||
goldenrod_dark = (0.7216, 0.5255, 0.0431)
|
||||
goldenrod_light = (0.9804, 0.9804, 0.8235)
|
||||
goldenrod_pale = (0.9333, 0.9098, 0.6667)
|
||||
light_goldenrod = (0.9333, 0.8667, 0.5098)
|
||||
melon = (0.8900, 0.6600, 0.4100)
|
||||
naples_yellow_deep = (1.0000, 0.6600, 0.0700)
|
||||
yellow = (1.0000, 1.0000, 0.0000)
|
||||
yellow_light = (1.0000, 1.0000, 0.8784)
|
||||
|
||||
# Greens
|
||||
chartreuse = (0.4980, 1.0000, 0.0000)
|
||||
chrome_oxide_green = (0.4000, 0.5000, 0.0800)
|
||||
cinnabar_green = (0.3800, 0.7000, 0.1600)
|
||||
cobalt_green = (0.2400, 0.5700, 0.2500)
|
||||
emerald_green = (0.0000, 0.7900, 0.3400)
|
||||
forest_green = (0.1333, 0.5451, 0.1333)
|
||||
green = (0.0000, 1.0000, 0.0000)
|
||||
green_dark = (0.0000, 0.3922, 0.0000)
|
||||
green_pale = (0.5961, 0.9843, 0.5961)
|
||||
green_yellow = (0.6784, 1.0000, 0.1843)
|
||||
lawn_green = (0.4863, 0.9882, 0.0000)
|
||||
lime_green = (0.1961, 0.8039, 0.1961)
|
||||
mint = (0.7400, 0.9900, 0.7900)
|
||||
olive = (0.2300, 0.3700, 0.1700)
|
||||
olive_drab = (0.4196, 0.5569, 0.1373)
|
||||
olive_green_dark = (0.3333, 0.4196, 0.1843)
|
||||
permanent_green = (0.0400, 0.7900, 0.1700)
|
||||
sap_green = (0.1900, 0.5000, 0.0800)
|
||||
sea_green = (0.1804, 0.5451, 0.3412)
|
||||
sea_green_dark = (0.5608, 0.7373, 0.5608)
|
||||
sea_green_medium = (0.2353, 0.7020, 0.4431)
|
||||
sea_green_light = (0.1255, 0.6980, 0.6667)
|
||||
spring_green = (0.0000, 1.0000, 0.4980)
|
||||
spring_green_medium = (0.0000, 0.9804, 0.6039)
|
||||
terre_verte = (0.2200, 0.3700, 0.0600)
|
||||
viridian_light = (0.4300, 1.0000, 0.4400)
|
||||
yellow_green = (0.6039, 0.8039, 0.1961)
|
||||
|
||||
# Cyans
|
||||
aquamarine = (0.4980, 1.0000, 0.8314)
|
||||
aquamarine_medium = (0.4000, 0.8039, 0.6667)
|
||||
cyan = (0.0000, 1.0000, 1.0000)
|
||||
cyan_white = (0.8784, 1.0000, 1.0000)
|
||||
turquoise = (0.2510, 0.8784, 0.8157)
|
||||
turquoise_dark = (0.0000, 0.8078, 0.8196)
|
||||
turquoise_medium = (0.2824, 0.8196, 0.8000)
|
||||
turquoise_pale = (0.6863, 0.9333, 0.9333)
|
||||
|
||||
# Blues
|
||||
alice_blue = (0.9412, 0.9725, 1.0000)
|
||||
blue = (0.0000, 0.0000, 1.0000)
|
||||
blue_light = (0.6784, 0.8471, 0.9020)
|
||||
blue_medium = (0.0000, 0.0000, 0.8039)
|
||||
cadet = (0.3725, 0.6196, 0.6275)
|
||||
cobalt = (0.2400, 0.3500, 0.6700)
|
||||
cornflower = (0.3922, 0.5843, 0.9294)
|
||||
cerulean = (0.0200, 0.7200, 0.8000)
|
||||
dodger_blue = (0.1176, 0.5647, 1.0000)
|
||||
indigo = (0.0300, 0.1800, 0.3300)
|
||||
manganese_blue = (0.0100, 0.6600, 0.6200)
|
||||
midnight_blue = (0.0980, 0.0980, 0.4392)
|
||||
navy = (0.0000, 0.0000, 0.5020)
|
||||
peacock = (0.2000, 0.6300, 0.7900)
|
||||
powder_blue = (0.6902, 0.8784, 0.9020)
|
||||
royal_blue = (0.2549, 0.4118, 0.8824)
|
||||
slate_blue = (0.4157, 0.3529, 0.8039)
|
||||
slate_blue_dark = (0.2824, 0.2392, 0.5451)
|
||||
slate_blue_light = (0.5176, 0.4392, 1.0000)
|
||||
slate_blue_medium = (0.4824, 0.4078, 0.9333)
|
||||
sky_blue = (0.5294, 0.8078, 0.9216)
|
||||
sky_blue_deep = (0.0000, 0.7490, 1.0000)
|
||||
sky_blue_light = (0.5294, 0.8078, 0.9804)
|
||||
steel_blue = (0.2745, 0.5098, 0.7059)
|
||||
steel_blue_light = (0.6902, 0.7686, 0.8706)
|
||||
turquoise_blue = (0.0000, 0.7800, 0.5500)
|
||||
ultramarine = (0.0700, 0.0400, 0.5600)
|
||||
|
||||
# Magentas
|
||||
blue_violet = (0.5412, 0.1686, 0.8863)
|
||||
cobalt_violet_deep = (0.5700, 0.1300, 0.6200)
|
||||
magenta = (1.0000, 0.0000, 1.0000)
|
||||
orchid = (0.8549, 0.4392, 0.8392)
|
||||
orchid_dark = (0.6000, 0.1961, 0.8000)
|
||||
orchid_medium = (0.7294, 0.3333, 0.8275)
|
||||
permanent_red_violet = (0.8600, 0.1500, 0.2700)
|
||||
plum = (0.8667, 0.6275, 0.8667)
|
||||
purple = (0.6275, 0.1255, 0.9412)
|
||||
purple_medium = (0.5765, 0.4392, 0.8588)
|
||||
ultramarine_violet = (0.3600, 0.1400, 0.4300)
|
||||
violet = (0.5600, 0.3700, 0.6000)
|
||||
violet_dark = (0.5804, 0.0000, 0.8275)
|
||||
violet_red = (0.8157, 0.1255, 0.5647)
|
||||
violet_red_medium = (0.7804, 0.0824, 0.5216)
|
||||
violet_red_pale = (0.8588, 0.4392, 0.5765)
|
||||
@@ -0,0 +1,861 @@
|
||||
"""This module provides classes that allow numpy style access
|
||||
to VTK datasets. See examples at bottom.
|
||||
"""
|
||||
|
||||
from contextlib import suppress
|
||||
from vtkmodules.vtkCommonCore import vtkPoints, vtkAbstractArray, vtkDataArray
|
||||
from vtkmodules.vtkCommonDataModel import (
|
||||
vtkCellArray,
|
||||
vtkDataObject,
|
||||
vtkFieldData,
|
||||
vtkDataSetAttributes,
|
||||
vtkPointData,
|
||||
vtkCellData,
|
||||
vtkDataObject,
|
||||
vtkImageData,
|
||||
vtkMultiBlockDataSet,
|
||||
vtkPolyData,
|
||||
vtkStructuredGrid,
|
||||
vtkRectilinearGrid,
|
||||
vtkUnstructuredGrid,
|
||||
vtkOverlappingAMR,
|
||||
vtkPartitionedDataSet,
|
||||
vtkPartitionedDataSetCollection,
|
||||
)
|
||||
|
||||
import weakref
|
||||
|
||||
NUMPY_AVAILABLE = False
|
||||
|
||||
with suppress(ImportError):
|
||||
import numpy
|
||||
from vtkmodules.numpy_interface import dataset_adapter as dsa
|
||||
|
||||
NUMPY_AVAILABLE = True
|
||||
|
||||
|
||||
class FieldDataBase(object):
|
||||
def __init__(self):
|
||||
self.association = None
|
||||
self.dataset = None
|
||||
|
||||
def __getitem__(self, idx):
|
||||
"""Implements the [] operator. Accepts an array name or index."""
|
||||
return self.get_array(idx)
|
||||
|
||||
def __setitem__(self, name, value):
|
||||
"""Implements the [] operator. Accepts an array name or index."""
|
||||
return self.set_array(name, value)
|
||||
|
||||
def get_array(self, idx):
|
||||
"Given an index or name, returns a VTKArray."
|
||||
if isinstance(idx, int) and idx >= self.GetNumberOfArrays():
|
||||
raise IndexError("array index out of range")
|
||||
vtkarray = super().GetArray(idx)
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
return vtkarray if vtkarray else self.GetAbstractArray(idx)
|
||||
|
||||
if not vtkarray:
|
||||
vtkarray = self.GetAbstractArray(idx)
|
||||
if vtkarray:
|
||||
return vtkarray
|
||||
return dsa.NoneArray
|
||||
array = dsa.vtkDataArrayToVTKArray(vtkarray, self.dataset)
|
||||
array.Association = self.association
|
||||
return array
|
||||
|
||||
def __contains__(self, aname):
|
||||
"""Returns true if the container contains arrays
|
||||
with the given name, false otherwise"""
|
||||
return self.HasArray(aname)
|
||||
|
||||
def keys(self):
|
||||
"""Returns the names of the arrays as a list."""
|
||||
kys = []
|
||||
narrays = self.GetNumberOfArrays()
|
||||
for i in range(narrays):
|
||||
name = self.GetAbstractArray(i).GetName()
|
||||
if name:
|
||||
kys.append(name)
|
||||
return tuple(kys)
|
||||
|
||||
def values(self):
|
||||
"""Returns the arrays as a tuple."""
|
||||
vals = []
|
||||
narrays = self.GetNumberOfArrays()
|
||||
for i in range(narrays):
|
||||
a = self.get_array(i)
|
||||
if a.GetName():
|
||||
vals.append(a)
|
||||
return tuple(vals)
|
||||
|
||||
def items(self):
|
||||
"""Returns a tuple of pairs (name, array)"""
|
||||
pairs = []
|
||||
narrays = self.GetNumberOfArrays()
|
||||
for i in range(narrays):
|
||||
arr = self.get_array(i)
|
||||
name = arr.GetName()
|
||||
if name:
|
||||
pairs.append((name, arr))
|
||||
return tuple(pairs)
|
||||
|
||||
def set_array(self, name, narray):
|
||||
"""Appends a new array to the dataset attributes."""
|
||||
if not NUMPY_AVAILABLE:
|
||||
if isinstance(narray, vtkAbstractArray):
|
||||
narray.SetName(name)
|
||||
self.AddArray(narray)
|
||||
return
|
||||
|
||||
if narray is dsa.NoneArray:
|
||||
# if NoneArray, nothing to do.
|
||||
return
|
||||
|
||||
if self.association == vtkDataObject.POINT:
|
||||
arrLength = self.dataset.GetNumberOfPoints()
|
||||
elif self.association == vtkDataObject.CELL:
|
||||
arrLength = self.dataset.GetNumberOfCells()
|
||||
elif (
|
||||
self.association == vtkDataObject.ROW
|
||||
and self.dataset.GetNumberOfColumns() > 0
|
||||
):
|
||||
arrLength = self.dataset.GetNumberOfRows()
|
||||
else:
|
||||
if not isinstance(narray, numpy.ndarray):
|
||||
arrLength = 1
|
||||
else:
|
||||
arrLength = narray.shape[0]
|
||||
|
||||
# Fixup input array length:
|
||||
if (
|
||||
not isinstance(narray, numpy.ndarray) or numpy.ndim(narray) == 0
|
||||
): # Scalar input
|
||||
dtype = narray.dtype if isinstance(narray, numpy.ndarray) else type(narray)
|
||||
tmparray = numpy.empty(arrLength, dtype=dtype)
|
||||
tmparray.fill(narray)
|
||||
narray = tmparray
|
||||
elif narray.shape[0] != arrLength: # Vector input
|
||||
components = 1
|
||||
for l in narray.shape:
|
||||
components *= l
|
||||
tmparray = numpy.empty((arrLength, components), dtype=narray.dtype)
|
||||
tmparray[:] = narray.flatten()
|
||||
narray = tmparray
|
||||
|
||||
shape = narray.shape
|
||||
|
||||
if len(shape) == 3:
|
||||
# Array of matrices. We need to make sure the order in memory is right.
|
||||
# If column order (c order), transpose. VTK wants row order (fortran
|
||||
# order). The deep copy later will make sure that the array is contiguous.
|
||||
# If row order but not contiguous, transpose so that the deep copy below
|
||||
# does not happen.
|
||||
size = narray.dtype.itemsize
|
||||
if (narray.strides[1] / size == 3 and narray.strides[2] / size == 1) or (
|
||||
narray.strides[1] / size == 1
|
||||
and narray.strides[2] / size == 3
|
||||
and not narray.flags.contiguous
|
||||
):
|
||||
narray = narray.transpose(0, 2, 1)
|
||||
|
||||
# If array is not contiguous, make a deep copy that is contiguous
|
||||
if not narray.flags.contiguous:
|
||||
narray = numpy.ascontiguousarray(narray)
|
||||
|
||||
# Flatten array of matrices to array of vectors
|
||||
if len(shape) == 3:
|
||||
narray = narray.reshape(shape[0], shape[1] * shape[2])
|
||||
|
||||
# this handle the case when an input array is directly appended on the
|
||||
# output. We want to make sure that the array added to the output is not
|
||||
# referring to the input dataset.
|
||||
copy = dsa.VTKArray(narray)
|
||||
try:
|
||||
copy.VTKObject = narray.VTKObject
|
||||
except AttributeError:
|
||||
pass
|
||||
arr = dsa.numpyTovtkDataArray(copy, name)
|
||||
self.AddArray(arr)
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
"""Test dict-like equivalency."""
|
||||
# here we check if other is the same class or a subclass of self.
|
||||
if not isinstance(other, type(self)):
|
||||
return False
|
||||
|
||||
if self is other:
|
||||
return True
|
||||
|
||||
"""
|
||||
If numpy is not available, only check for identity without comparing contents of the data arrays
|
||||
"""
|
||||
if not NUMPY_AVAILABLE:
|
||||
return False
|
||||
|
||||
if set(self.keys()) != set(other.keys()):
|
||||
return False
|
||||
|
||||
# verify the value of the arrays
|
||||
for key, value in other.items():
|
||||
if not numpy.array_equal(value, self[key]):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.keys())
|
||||
|
||||
def __len__(self):
|
||||
return self.GetNumberOfArrays()
|
||||
|
||||
@vtkFieldData.override
|
||||
class FieldData(FieldDataBase, vtkFieldData):
|
||||
pass
|
||||
|
||||
|
||||
class DataSetAttributesBase(FieldDataBase):
|
||||
pass
|
||||
|
||||
|
||||
@vtkDataSetAttributes.override
|
||||
class DataSetAttributes(DataSetAttributesBase, vtkDataSetAttributes):
|
||||
def __eq__(self, other: object) -> bool:
|
||||
"""Test dict-like equivalency."""
|
||||
if not super().__eq__(other):
|
||||
return False
|
||||
|
||||
for attr in [
|
||||
"GetScalars",
|
||||
"GetVectors",
|
||||
"GetNormals",
|
||||
"GetTangents",
|
||||
"GetTCoords",
|
||||
"GetTensors",
|
||||
"GetGlobalIds",
|
||||
"GetPedigreeIds",
|
||||
"GetRationalWeights",
|
||||
"GetHigherOrderDegrees",
|
||||
"GetProcessIds",
|
||||
]:
|
||||
self_attr = getattr(self, attr)()
|
||||
other_attr = getattr(other, attr)()
|
||||
if self_attr and other_attr:
|
||||
if self_attr.GetName() != other_attr.GetName():
|
||||
return False
|
||||
elif self_attr != other_attr:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@vtkPointData.override
|
||||
class PointData(DataSetAttributesBase, vtkPointData):
|
||||
pass
|
||||
|
||||
|
||||
@vtkCellData.override
|
||||
class CellData(DataSetAttributesBase, vtkCellData):
|
||||
pass
|
||||
|
||||
|
||||
class CompositeDataSetAttributesIterator(object):
|
||||
def __init__(self, cdsa):
|
||||
self._cdsa = cdsa
|
||||
if cdsa:
|
||||
self._itr = iter(cdsa.keys())
|
||||
else:
|
||||
self._itr = None
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if not self._cdsa:
|
||||
raise StopIteration
|
||||
|
||||
name = next(self._itr)
|
||||
return self._cdsa[name]
|
||||
|
||||
def next(self):
|
||||
return self.__next__()
|
||||
|
||||
|
||||
class CompositeDataSetAttributes(object):
|
||||
"""This is a python friendly wrapper for vtkDataSetAttributes for composite
|
||||
datasets. Since composite datasets themselves don't have attribute data, but
|
||||
the attribute data is associated with the leaf nodes in the composite
|
||||
dataset, this class simulates a DataSetAttributes interface by taking a
|
||||
union of DataSetAttributes associated with all leaf nodes."""
|
||||
|
||||
def __init__(self, dataset, association):
|
||||
self.DataSet = dataset
|
||||
self.Association = association
|
||||
self.ArrayNames = []
|
||||
self.Arrays = {}
|
||||
|
||||
# build the set of arrays available in the composite dataset. Since
|
||||
# composite datasets can have partial arrays, we need to iterate over
|
||||
# all non-null blocks in the dataset.
|
||||
self.__determine_arraynames()
|
||||
|
||||
def __determine_arraynames(self):
|
||||
array_set = set()
|
||||
array_list = []
|
||||
for dataset in self.DataSet:
|
||||
dsa = dataset.GetAttributesAsFieldData(self.Association)
|
||||
for array_name in dsa.keys():
|
||||
if array_name not in array_set:
|
||||
array_set.add(array_name)
|
||||
array_list.append(array_name)
|
||||
self.ArrayNames = array_list
|
||||
|
||||
def modified(self):
|
||||
"""Rescans the contained dataset to update the
|
||||
internal list of arrays."""
|
||||
self.__determine_arraynames()
|
||||
|
||||
def __contains__(self, aname):
|
||||
"""Returns true if the container contains arrays
|
||||
with the given name, false otherwise"""
|
||||
return aname in self.ArrayNames
|
||||
|
||||
def keys(self):
|
||||
"""Returns the names of the arrays as a tuple."""
|
||||
return tuple(self.ArrayNames)
|
||||
|
||||
def values(self):
|
||||
"""Returns all the arrays as a tuple."""
|
||||
arrays = []
|
||||
for array in self:
|
||||
arrays.append(array)
|
||||
return tuple(arrays)
|
||||
|
||||
def items(self):
|
||||
"""Returns (name, array) pairs as a tuple."""
|
||||
items = []
|
||||
for name in self.keys():
|
||||
items.append((name, self[name]))
|
||||
return tuple(items)
|
||||
|
||||
def __getitem__(self, idx):
|
||||
"""Implements the [] operator. Accepts an array name."""
|
||||
return self.get_array(idx)
|
||||
|
||||
def __setitem__(self, name, narray):
|
||||
"""Implements the [] operator. Accepts an array name."""
|
||||
return self.set_array(name, narray)
|
||||
|
||||
def set_array(self, name, narray):
|
||||
"""Appends a new array to the composite dataset attributes."""
|
||||
if not NUMPY_AVAILABLE:
|
||||
# don't know how to handle composite dataset attribute when numpy not around
|
||||
raise NotImplementedError("Only available with numpy")
|
||||
|
||||
if narray is dsa.NoneArray:
|
||||
# if NoneArray, nothing to do.
|
||||
return
|
||||
|
||||
added = False
|
||||
if not isinstance(narray, dsa.VTKCompositeDataArray): # Scalar input
|
||||
for ds in self.DataSet:
|
||||
ds.GetAttributesAsFieldData(self.Association).set_array(name, narray)
|
||||
added = True
|
||||
if added:
|
||||
self.ArrayNames.append(name)
|
||||
# don't add the narray since it's a scalar. GetArray() will create a
|
||||
# VTKCompositeArray on-demand.
|
||||
else:
|
||||
for ds, array in zip(self.DataSet, narray.Arrays):
|
||||
if array is not None:
|
||||
ds.GetAttributesAsFieldData(self.Association).set_array(name, array)
|
||||
added = True
|
||||
if added:
|
||||
self.ArrayNames.append(name)
|
||||
self.Arrays[name] = weakref.ref(narray)
|
||||
|
||||
def get_array(self, idx):
|
||||
"""Given a name, returns a VTKCompositeArray."""
|
||||
arrayname = idx
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
# don't know how to handle composite dataset attribute when numpy not around
|
||||
raise NotImplementedError("Only available with numpy")
|
||||
|
||||
if arrayname not in self.ArrayNames:
|
||||
return dsa.NoneArray
|
||||
if arrayname not in self.Arrays or self.Arrays[arrayname]() is None:
|
||||
array = dsa.VTKCompositeDataArray(
|
||||
dataset=self.DataSet, name=arrayname, association=self.Association
|
||||
)
|
||||
self.Arrays[arrayname] = weakref.ref(array)
|
||||
else:
|
||||
array = self.Arrays[arrayname]()
|
||||
return array
|
||||
|
||||
def __iter__(self):
|
||||
"""Iterators on keys"""
|
||||
return iter(self.ArrayNames)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.ArrayNames)
|
||||
|
||||
# class DataSet(DataObjectBase):
|
||||
class DataSet(object):
|
||||
def __init__(self, **kwargs) -> None:
|
||||
self._numpy_attrs = []
|
||||
|
||||
@property
|
||||
def point_data(self):
|
||||
pd = super().GetPointData()
|
||||
pd.dataset = self
|
||||
pd.association = self.POINT
|
||||
return pd
|
||||
|
||||
@property
|
||||
def cell_data(self):
|
||||
cd = super().GetCellData()
|
||||
cd.dataset = self
|
||||
cd.association = self.CELL
|
||||
return cd
|
||||
|
||||
@property
|
||||
def field_data(self):
|
||||
fd = super().GetFieldData()
|
||||
if fd:
|
||||
fd.dataset = self
|
||||
fd.association = self.FIELD
|
||||
return fd
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
"""Test equivalency between data objects."""
|
||||
if not isinstance(self, type(other)):
|
||||
return False
|
||||
|
||||
if self is other:
|
||||
return True
|
||||
|
||||
"""
|
||||
If numpy is not available, only check for identity without comparing contents of the data arrays
|
||||
"""
|
||||
if not NUMPY_AVAILABLE:
|
||||
return False
|
||||
|
||||
for attr in self._numpy_attrs:
|
||||
if hasattr(self, attr):
|
||||
if not numpy.array_equal(getattr(self, attr), getattr(other, attr)):
|
||||
return False
|
||||
|
||||
for attr in ["field_data", "point_data", "cell_data"]:
|
||||
if getattr(self, attr) != getattr(other, attr):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def convert_to_unstructured_grid(self):
|
||||
from vtkmodules.vtkFiltersCore import vtkExtractCells
|
||||
|
||||
ecells = vtkExtractCells()
|
||||
ecells.SetInputData(self)
|
||||
ecells.ExtractAllCellsOn()
|
||||
ecells.Update()
|
||||
return ecells.GetOutput()
|
||||
|
||||
|
||||
class PointSet(DataSet):
|
||||
def __init__(self, **kwargs) -> None:
|
||||
DataSet.__init__(self, **kwargs)
|
||||
self._numpy_attrs.append("points")
|
||||
|
||||
@property
|
||||
def points(self):
|
||||
pts = self.GetPoints()
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
return pts
|
||||
|
||||
if not pts or not pts.GetData():
|
||||
return None
|
||||
return dsa.vtkDataArrayToVTKArray(pts.GetData())
|
||||
|
||||
@points.setter
|
||||
def points(self, points):
|
||||
if isinstance(points, vtkPoints):
|
||||
self.SetPoints(points)
|
||||
return
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
raise ValueError("Expect vtkPoints")
|
||||
|
||||
pts = dsa.numpyTovtkDataArray(points, "points")
|
||||
vtkpts = vtkPoints()
|
||||
vtkpts.SetData(pts)
|
||||
self.SetPoints(vtkpts)
|
||||
|
||||
|
||||
@vtkUnstructuredGrid.override
|
||||
class UnstructuredGrid(PointSet, vtkUnstructuredGrid):
|
||||
def __init__(self, **kwargs):
|
||||
PointSet.__init__(self, **kwargs)
|
||||
vtkUnstructuredGrid.__init__(self, **kwargs)
|
||||
|
||||
@property
|
||||
def cells(self):
|
||||
ca = self.GetCells()
|
||||
conn_vtk = ca.GetConnectivityArray()
|
||||
offsets_vtk = ca.GetOffsetsArray()
|
||||
ct_vtk = self.GetCellTypesArray()
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
return {
|
||||
"connectivity": conn_vtk,
|
||||
"offsets": offsets_vtk,
|
||||
"cell_types": ct_vtk,
|
||||
}
|
||||
|
||||
conn = dsa.vtkDataArrayToVTKArray(conn_vtk)
|
||||
offsets = dsa.vtkDataArrayToVTKArray(offsets_vtk)
|
||||
ct = dsa.vtkDataArrayToVTKArray(ct_vtk)
|
||||
return {"connectivity": conn, "offsets": offsets, "cell_types": ct}
|
||||
|
||||
@cells.setter
|
||||
def cells(self, cells):
|
||||
ca = vtkCellArray()
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
ca.SetData(cells["offsets"], cells["connectivity"])
|
||||
self.SetCells(cells["cell_types"], ca)
|
||||
return
|
||||
|
||||
conn_vtk = dsa.numpyTovtkDataArray(cells["connectivity"])
|
||||
offsets_vtk = dsa.numpyTovtkDataArray(cells["offsets"])
|
||||
cell_types_vtk = dsa.numpyTovtkDataArray(cells["cell_types"])
|
||||
ca.SetData(offsets_vtk, conn_vtk)
|
||||
self.SetCells(cell_types_vtk, ca)
|
||||
|
||||
|
||||
@vtkImageData.override
|
||||
class ImageData(DataSet, vtkImageData):
|
||||
def __init__(self, **kwargs):
|
||||
DataSet.__init__(self, **kwargs)
|
||||
vtkImageData.__init__(self, **kwargs)
|
||||
|
||||
|
||||
@vtkPolyData.override
|
||||
class PolyData(PointSet, vtkPolyData):
|
||||
def __init__(self, **kwargs) -> None:
|
||||
PointSet.__init__(self, **kwargs)
|
||||
vtkPolyData.__init__(self, **kwargs)
|
||||
self._numpy_attrs.extend(["verts", "lines", "strips", "polys"])
|
||||
|
||||
@property
|
||||
def verts_arrays(self):
|
||||
ca = self.GetVerts()
|
||||
conn_vtk = ca.GetConnectivityArray()
|
||||
offsets_vtk = ca.GetOffsetsArray()
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
return {
|
||||
"connectivity": conn_vtk,
|
||||
"offsets": offsets_vtk,
|
||||
}
|
||||
|
||||
conn = dsa.vtkDataArrayToVTKArray(conn_vtk)
|
||||
offsets = dsa.vtkDataArrayToVTKArray(offsets_vtk)
|
||||
return {"connectivity": conn, "offsets": offsets}
|
||||
|
||||
@property
|
||||
def lines_arrays(self):
|
||||
ca = self.GetLines()
|
||||
conn_vtk = ca.GetConnectivityArray()
|
||||
offsets_vtk = ca.GetOffsetsArray()
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
return {
|
||||
"connectivity": conn_vtk,
|
||||
"offsets": offsets_vtk,
|
||||
}
|
||||
|
||||
conn = dsa.vtkDataArrayToVTKArray(conn_vtk)
|
||||
offsets = dsa.vtkDataArrayToVTKArray(offsets_vtk)
|
||||
return {"connectivity": conn, "offsets": offsets}
|
||||
|
||||
@property
|
||||
def strips_arrays(self):
|
||||
ca = self.GetStrips()
|
||||
conn_vtk = ca.GetConnectivityArray()
|
||||
offsets_vtk = ca.GetOffsetsArray()
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
return {
|
||||
"connectivity": conn_vtk,
|
||||
"offsets": offsets_vtk,
|
||||
}
|
||||
|
||||
conn = dsa.vtkDataArrayToVTKArray(conn_vtk)
|
||||
offsets = dsa.vtkDataArrayToVTKArray(offsets_vtk)
|
||||
return {"connectivity": conn, "offsets": offsets}
|
||||
|
||||
@property
|
||||
def polys_arrays(self):
|
||||
ca = self.GetPolys()
|
||||
conn_vtk = ca.GetConnectivityArray()
|
||||
offsets_vtk = ca.GetOffsetsArray()
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
return {
|
||||
"connectivity": conn_vtk,
|
||||
"offsets": offsets_vtk,
|
||||
}
|
||||
|
||||
conn = dsa.vtkDataArrayToVTKArray(conn_vtk)
|
||||
offsets = dsa.vtkDataArrayToVTKArray(offsets_vtk)
|
||||
return {"connectivity": conn, "offsets": offsets}
|
||||
|
||||
|
||||
@vtkRectilinearGrid.override
|
||||
class RectilinearGrid(DataSet, vtkRectilinearGrid):
|
||||
def __init__(self, **kwargs) -> None:
|
||||
DataSet.__init__(self, **kwargs)
|
||||
vtkRectilinearGrid.__init__(self, **kwargs)
|
||||
self._numpy_attrs.extend(["x_coordinates", "y_coordinates", "z_coordinates"])
|
||||
|
||||
@property
|
||||
def x_coordinates(self):
|
||||
pts = self.GetXCoordinates()
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
return pts
|
||||
|
||||
if not pts:
|
||||
return None
|
||||
return dsa.vtkDataArrayToVTKArray(pts)
|
||||
|
||||
@x_coordinates.setter
|
||||
def x_coordinates(self, points):
|
||||
if isinstance(points, vtkDataArray):
|
||||
self.SetXCoordinates(points)
|
||||
return
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
raise ValueError("Expect vtkDataArray")
|
||||
|
||||
pts = dsa.numpyTovtkDataArray(points, "x_coords")
|
||||
self.SetXCoordinates(pts)
|
||||
|
||||
@property
|
||||
def y_coordinates(self):
|
||||
pts = self.GetYCoordinates()
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
return pts
|
||||
|
||||
if not pts:
|
||||
return None
|
||||
return dsa.vtkDataArrayToVTKArray(pts)
|
||||
|
||||
@y_coordinates.setter
|
||||
def y_coordinates(self, points):
|
||||
if isinstance(points, vtkDataArray):
|
||||
self.SetYCoordinates(points)
|
||||
return
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
raise ValueError("Expect vtkDataArray")
|
||||
|
||||
pts = dsa.numpyTovtkDataArray(points, "y_coords")
|
||||
self.SetYCoordinates(pts)
|
||||
|
||||
@property
|
||||
def z_coordinates(self):
|
||||
pts = self.GetZCoordinates()
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
return pts
|
||||
|
||||
if not pts:
|
||||
return None
|
||||
return dsa.vtkDataArrayToVTKArray(pts)
|
||||
|
||||
@z_coordinates.setter
|
||||
def z_coordinates(self, points):
|
||||
if isinstance(points, vtkDataArray):
|
||||
self.SetZCoordinates(points)
|
||||
return
|
||||
|
||||
if not NUMPY_AVAILABLE:
|
||||
raise ValueError("Expect vtkDataArray")
|
||||
|
||||
pts = dsa.numpyTovtkDataArray(points, "z_coords")
|
||||
self.SetZCoordinates(pts)
|
||||
|
||||
|
||||
class CompositeDataIterator(object):
|
||||
"""Wrapper for a vtkCompositeDataIterator class to satisfy
|
||||
the python iterator protocol. This iterator iterates
|
||||
over non-empty leaf nodes. To iterate over empty or
|
||||
non-leaf nodes, use the vtkCompositeDataIterator directly.
|
||||
"""
|
||||
|
||||
def __init__(self, cds):
|
||||
self.Iterator = cds.NewIterator()
|
||||
if self.Iterator:
|
||||
self.Iterator.UnRegister(None)
|
||||
self.Iterator.GoToFirstItem()
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if not self.Iterator:
|
||||
raise StopIteration
|
||||
|
||||
if self.Iterator.IsDoneWithTraversal():
|
||||
raise StopIteration
|
||||
retVal = self.Iterator.GetCurrentDataObject()
|
||||
self.Iterator.GoToNextItem()
|
||||
return retVal
|
||||
|
||||
def next(self):
|
||||
return self.__next__()
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Returns attributes from the vtkCompositeDataIterator."""
|
||||
return getattr(self.Iterator, name)
|
||||
|
||||
|
||||
class CompositeDataSetBase(object):
|
||||
"""A wrapper for vtkCompositeData and subclasses that makes it easier
|
||||
to access Point/Cell/Field data as VTKCompositeDataArrays. It also
|
||||
provides a Python type iterator."""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._PointData = None
|
||||
self._CellData = None
|
||||
self._FieldData = None
|
||||
self._Points = None
|
||||
|
||||
def __iter__(self):
|
||||
"Creates an iterator for the contained datasets."
|
||||
return CompositeDataIterator(self)
|
||||
|
||||
def get_attributes(self, type):
|
||||
"""Returns the attributes specified by the type as a
|
||||
CompositeDataSetAttributes instance."""
|
||||
return CompositeDataSetAttributes(self, type)
|
||||
|
||||
@property
|
||||
def point_data(self):
|
||||
"Returns the point data as a DataSetAttributes instance."
|
||||
if self._PointData is None or self._PointData() is None:
|
||||
pdata = self.get_attributes(vtkDataObject.POINT)
|
||||
self._PointData = weakref.ref(pdata)
|
||||
return self._PointData()
|
||||
|
||||
@property
|
||||
def cell_data(self):
|
||||
"Returns the cell data as a DataSetAttributes instance."
|
||||
if self._CellData is None or self._CellData() is None:
|
||||
cdata = self.get_attributes(vtkDataObject.CELL)
|
||||
self._CellData = weakref.ref(cdata)
|
||||
return self._CellData()
|
||||
|
||||
@property
|
||||
def field_data(self):
|
||||
"Returns the field data as a DataSetAttributes instance."
|
||||
if self._FieldData is None or self._FieldData() is None:
|
||||
fdata = self.get_attributes(vtkDataObject.FIELD)
|
||||
self._FieldData = weakref.ref(fdata)
|
||||
return self._FieldData()
|
||||
|
||||
@property
|
||||
def points(self):
|
||||
"Returns the points as a VTKCompositeDataArray instance."
|
||||
if not NUMPY_AVAILABLE:
|
||||
# don't know how to handle composite dataset when numpy not around
|
||||
raise NotImplementedError("Only available with numpy")
|
||||
|
||||
if self._Points is None or self._Points() is None:
|
||||
pts = []
|
||||
for ds in self:
|
||||
try:
|
||||
_pts = ds.Points
|
||||
except AttributeError:
|
||||
_pts = None
|
||||
|
||||
if _pts is None:
|
||||
pts.append(dsa.NoneArray)
|
||||
else:
|
||||
pts.append(_pts)
|
||||
if len(pts) == 0 or all([a is dsa.NoneArray for a in pts]):
|
||||
cpts = dsa.NoneArray
|
||||
else:
|
||||
cpts = dsa.VTKCompositeDataArray(pts, dataset=self)
|
||||
self._Points = weakref.ref(cpts)
|
||||
return self._Points()
|
||||
|
||||
|
||||
@vtkPartitionedDataSet.override
|
||||
class PartitionedDataSet(CompositeDataSetBase, vtkPartitionedDataSet):
|
||||
def append(self, dataset):
|
||||
self.SetPartition(self.GetNumberOfPartitions(), dataset)
|
||||
|
||||
@vtkPartitionedDataSetCollection.override
|
||||
class PartitionedDataSetCollection(CompositeDataSetBase, vtkPartitionedDataSetCollection):
|
||||
def append(self, dataset):
|
||||
self.SetPartitionedDataSet(self.GetNumberOfPartitionedDataSets(), dataset)
|
||||
|
||||
@vtkOverlappingAMR.override
|
||||
class OverlappingAMR(CompositeDataSetBase, vtkOverlappingAMR):
|
||||
pass
|
||||
|
||||
@vtkMultiBlockDataSet.override
|
||||
class MultiBlockDataSet(CompositeDataSetBase, vtkMultiBlockDataSet):
|
||||
pass
|
||||
|
||||
@vtkStructuredGrid.override
|
||||
class StructuredGrid(PointSet, vtkStructuredGrid):
|
||||
def __init__(self, **kwargs):
|
||||
PointSet.__init__(self, **kwargs)
|
||||
vtkStructuredGrid.__init__(self, **kwargs)
|
||||
|
||||
@property
|
||||
def x_coordinates(self):
|
||||
if not NUMPY_AVAILABLE:
|
||||
raise NotImplementedError("Only available with numpy")
|
||||
|
||||
dims = [0,0,0]
|
||||
self.GetDimensions(dims)
|
||||
return self.points[:, 0].reshape(dims, order="F")
|
||||
|
||||
@property
|
||||
def y_coordinates(self):
|
||||
if not NUMPY_AVAILABLE:
|
||||
raise NotImplementedError("Only available with numpy")
|
||||
|
||||
dims = [0,0,0]
|
||||
self.GetDimensions(dims)
|
||||
return self.points[:, 1].reshape(dims, order="F")
|
||||
|
||||
@property
|
||||
def z_coordinates(self):
|
||||
if not NUMPY_AVAILABLE:
|
||||
raise NotImplementedError("Only available with numpy")
|
||||
dims = [0,0,0]
|
||||
self.GetDimensions(dims)
|
||||
return self.points[:, 2].reshape(dims, order="F")
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Handle pickle registration
|
||||
# -----------------------------------------------------------------------------
|
||||
with suppress(ImportError):
|
||||
import copyreg
|
||||
from vtkmodules.util.pickle_support import serialize_VTK_data_object
|
||||
|
||||
copyreg.pickle(PolyData, serialize_VTK_data_object)
|
||||
copyreg.pickle(UnstructuredGrid, serialize_VTK_data_object)
|
||||
copyreg.pickle(ImageData, serialize_VTK_data_object)
|
||||
copyreg.pickle(PartitionedDataSet, serialize_VTK_data_object)
|
||||
copyreg.pickle(StructuredGrid, serialize_VTK_data_object)
|
||||
@@ -0,0 +1,268 @@
|
||||
"""Utility classes to help with the simpler Python interface
|
||||
for connecting and executing pipelines."""
|
||||
|
||||
__all__ = ['select_ports', 'Pipeline', 'Output']
|
||||
|
||||
def _call(first, last, inp=None, port=0):
|
||||
"""Set the input of the first filter, update the pipeline
|
||||
and return the output."""
|
||||
if inp and not first.GetNumberOfInputPorts():
|
||||
raise ValueError(f"{first.GetClassName()} does not have input ports yet an input was passed to the pipeline.")
|
||||
in_cons = []
|
||||
if first.GetNumberOfInputPorts():
|
||||
n_cons = first.GetNumberOfInputConnections(port)
|
||||
for i in range(n_cons):
|
||||
op = first.GetInputConnection(port, i)
|
||||
if op and op.GetProducer():
|
||||
op.GetProducer().Register(None)
|
||||
in_cons.append(op)
|
||||
from vtkmodules.vtkCommonExecutionModel import vtkAlgorithm
|
||||
from vtkmodules.vtkCommonExecutionModel import vtkTrivialProducer
|
||||
from collections.abc import Sequence
|
||||
if isinstance(inp, Sequence):
|
||||
if first.GetInputPortInformation(port).Has(
|
||||
vtkAlgorithm.INPUT_IS_REPEATABLE()):
|
||||
first.RemoveAllInputConnections(port)
|
||||
for aInp in inp:
|
||||
tp = vtkTrivialProducer()
|
||||
tp.SetOutput(aInp)
|
||||
first.AddInputConnection(port, tp.GetOutputPort());
|
||||
else:
|
||||
tp = vtkTrivialProducer()
|
||||
tp.SetOutput(inp)
|
||||
first.SetInputConnection(port, tp.GetOutputPort());
|
||||
|
||||
output = last.update().output
|
||||
|
||||
if first.GetNumberOfInputPorts():
|
||||
first.RemoveAllInputConnections(port)
|
||||
for op in in_cons:
|
||||
first.AddInputConnection(port, op)
|
||||
if op and op.GetProducer():
|
||||
op.GetProducer().UnRegister(None)
|
||||
|
||||
output_copy = []
|
||||
if type(output) is not tuple:
|
||||
output = (output,)
|
||||
for opt in output:
|
||||
copy = opt.NewInstance()
|
||||
copy.ShallowCopy(opt)
|
||||
output_copy.append(copy)
|
||||
if len(output_copy) == 1:
|
||||
return output_copy[0]
|
||||
else:
|
||||
return tuple(output_copy)
|
||||
|
||||
|
||||
class select_ports(object):
|
||||
"""Helper class for selecting input and output ports when
|
||||
connecting pipeline objects with the >> operator.
|
||||
Example uses:
|
||||
# Connect a source to the second input of a filter.
|
||||
source >> select_ports(1, filter)
|
||||
# Connect the second output of a source to a filter.
|
||||
select_ports(source, 1) >> filter
|
||||
# Combination of both: Connect source to second
|
||||
# input of the filter, then connect the second
|
||||
# output of that filter to another one.
|
||||
source >>> select_ports(1, filter, 1) >> filter2
|
||||
"""
|
||||
def __init__(self, *args):
|
||||
"""This constructor takes 2 or 3 arguments.
|
||||
The possibilities are:
|
||||
select_ports(input_port, algorithm)
|
||||
select_ports(algorithm, output_port)
|
||||
select_ports(input_port, algorithm, output_port)
|
||||
"""
|
||||
nargs = len(args)
|
||||
if nargs < 2 or nargs > 3:
|
||||
raise ValueError("Expecting 2 or 3 arguments")
|
||||
|
||||
self.input_port = None
|
||||
self.output_port = None
|
||||
before_alg = True
|
||||
for arg in args:
|
||||
if hasattr(arg, "IsA") and arg.IsA("vtkAlgorithm"):
|
||||
self.algorithm = arg
|
||||
before_alg = False
|
||||
else:
|
||||
if before_alg:
|
||||
self.input_port = arg
|
||||
else:
|
||||
self.output_port = arg
|
||||
if not self.input_port:
|
||||
self.input_port = 0
|
||||
if not self.output_port:
|
||||
self.output_port = 0
|
||||
|
||||
def SetInputConnection(self, inp):
|
||||
"Forwards to underlying algorithm and port."
|
||||
self.algorithm.SetInputConnection(self.input_port, inp)
|
||||
|
||||
def AddInputConnection(self, inp):
|
||||
"Forwards to underlying algorithm and port."
|
||||
self.algorithm.AddInputConnection(self.input_port, inp)
|
||||
|
||||
def GetOutputPort(self):
|
||||
"Returns the output port of the underlying algorithm."
|
||||
return self.algorithm.GetOutputPort(self.output_port)
|
||||
|
||||
def GetInputPortInformation(self, port):
|
||||
return self.algorithm.GetInputPortInformation(self.input_port)
|
||||
|
||||
def update(self):
|
||||
"""Execute the algorithm and return the output from the selected
|
||||
output port."""
|
||||
return self.algorithm.update()
|
||||
|
||||
def __rshift__(self, rhs):
|
||||
"Creates a pipeline between the underlying port and an algorithm."
|
||||
return Pipeline(self, rhs)
|
||||
|
||||
def __rrshift__(self, lhs):
|
||||
"""Creates a pipeline between the underlying port and an algorithm.
|
||||
This is to handle sequence >> select_ports where the port can
|
||||
accept multiple connections."""
|
||||
from collections.abc import Sequence
|
||||
if lhs is None or (isinstance(lhs, Sequence) and len(lhs == 0)):
|
||||
self.algorithm.RemoveAllInputConnections(self.input_port)
|
||||
return self
|
||||
return Pipeline(lhs, self)
|
||||
|
||||
def __call__(self, inp=None):
|
||||
"""Executes the underlying algorithm by passing input data to
|
||||
the selected input port. Returns a single output or a tuple
|
||||
if there are multiple outputs."""
|
||||
return _call(self.algorithm, self.algorithm, inp, self.input_port)
|
||||
|
||||
class Pipeline(object):
|
||||
"""Pipeline objects are created when 2 or more algorithms are
|
||||
connected with the >> operator. They store the first and last
|
||||
algorithms in the pipeline and enable connecting more algorithms
|
||||
and executing the pipeline. One should not have to create Pipeline
|
||||
objects directly. They are created by the use of the >> operator."""
|
||||
|
||||
PIPELINE = 0
|
||||
ALGORITHM = 1
|
||||
DATA = 2
|
||||
UNKNOWN = 3
|
||||
|
||||
def __init__(self, lhs, rhs):
|
||||
"""Create a pipeline object that connects two objects of the
|
||||
following type: data object, pipeline object, algorithm object."""
|
||||
left_type = self._determine_type(lhs)
|
||||
right_type = self._determine_type(rhs)
|
||||
if right_type == Pipeline.ALGORITHM:
|
||||
rhs_alg = rhs
|
||||
elif right_type == Pipeline.PIPELINE:
|
||||
rhs_alg = rhs.first
|
||||
else:
|
||||
raise TypeError(
|
||||
f"unsupported operand type(s) for >>: {type(lhs).__name__} and {type(rhs).__name__}")
|
||||
|
||||
from collections.abc import Sequence
|
||||
if isinstance(lhs, Sequence):
|
||||
for inp in lhs:
|
||||
self._connect(inp, rhs, rhs_alg, "AddInputConnection")
|
||||
else:
|
||||
self._connect(lhs, rhs, rhs_alg, "SetInputConnection")
|
||||
|
||||
def _connect(self, lhs, rhs, rhs_alg, connect_method):
|
||||
from vtkmodules.vtkCommonExecutionModel import vtkAlgorithm
|
||||
inInfo = rhs_alg.GetInputPortInformation(0)
|
||||
if inInfo.Has(vtkAlgorithm.INPUT_IS_REPEATABLE()):
|
||||
connect_method = 'AddInputConnection'
|
||||
|
||||
left_type = self._determine_type(lhs)
|
||||
right_type = self._determine_type(rhs)
|
||||
if left_type == Pipeline.UNKNOWN:
|
||||
raise TypeError(
|
||||
f"unsupported operand type(s) for >>: {type(lhs).__name__} and {type(rhs).__name__}")
|
||||
if right_type == Pipeline.ALGORITHM:
|
||||
if left_type == Pipeline.ALGORITHM:
|
||||
getattr(rhs_alg, connect_method)(lhs.GetOutputPort())
|
||||
self.first = lhs
|
||||
self.last = rhs
|
||||
elif left_type == Pipeline.PIPELINE:
|
||||
getattr(rhs_alg, connect_method)(lhs.last.GetOutputPort())
|
||||
self.first = lhs.first
|
||||
self.last = rhs
|
||||
elif left_type == Pipeline.DATA:
|
||||
from vtkmodules.vtkCommonExecutionModel import vtkTrivialProducer
|
||||
source = vtkTrivialProducer()
|
||||
source.SetOutput(lhs)
|
||||
getattr(rhs_alg, connect_method)(source.GetOutputPort())
|
||||
self.first = source
|
||||
self.last = rhs
|
||||
elif right_type == Pipeline.PIPELINE:
|
||||
if left_type == Pipeline.ALGORITHM:
|
||||
self.first = lhs
|
||||
self.last = rhs.last
|
||||
getattr(rhs_alg, connect_method)(lhs.GetOutputPort())
|
||||
elif left_type == Pipeline.PIPELINE:
|
||||
getattr(rhs_alg, connect_method)(lhs.last.GetOutputPort())
|
||||
self.first = lhs.first
|
||||
self.last = rhs.last
|
||||
elif left_type == Pipeline.DATA:
|
||||
from vtkmodules.vtkCommonExecutionModel import vtkTrivialProducer
|
||||
source = vtkTrivialProducer()
|
||||
source.SetOutput(lhs)
|
||||
getattr(rhs_alg, connect_method)(source.GetOutputPort())
|
||||
self.first = source
|
||||
self.last = rhs.last
|
||||
|
||||
def _determine_type(self, arg):
|
||||
if type(arg) is Pipeline:
|
||||
return Pipeline.PIPELINE
|
||||
if hasattr(arg, "SetInputConnection"):
|
||||
return Pipeline.ALGORITHM
|
||||
if hasattr(arg, "IsA") and arg.IsA("vtkDataObject"):
|
||||
return Pipeline.DATA
|
||||
return Pipeline.UNKNOWN
|
||||
|
||||
def update(self, **kwargs):
|
||||
"""Update the pipeline and return the last algorithm's
|
||||
output."""
|
||||
return self.last.update()
|
||||
|
||||
def __call__(self, inp=None):
|
||||
"""Sets the input of the first filter, update the pipeline
|
||||
and returns the output. A single data object or a tuple
|
||||
of data objects (when there are multiple outputs) are
|
||||
returned."""
|
||||
return _call(self.first, self.last, inp)
|
||||
|
||||
def __rshift__(self, rhs):
|
||||
"""Used to connect two pipeline items. The left side can
|
||||
be a data object, an algorithm or a pipeline. The right
|
||||
side can be an algorithm or a pipeline."""
|
||||
return Pipeline(self, rhs)
|
||||
|
||||
def __rrshift__(self, lhs):
|
||||
"""Creates a pipeline between a sequence input and a pipeline."""
|
||||
from collections.abc import Sequence
|
||||
if lhs is None or (isinstance(lhs, Sequence) and len(lhs) == 0):
|
||||
self.first.RemoveAllInputConnections(0)
|
||||
return self
|
||||
return Pipeline(lhs, self)
|
||||
|
||||
class Output(object):
|
||||
"""Helper object to represent the output of an algorithms as
|
||||
returned by the update() method. Implements the output property
|
||||
enabling calling update().output."""
|
||||
def __init__(self, algorithm, **kwargs):
|
||||
self.algorithm = algorithm
|
||||
self.algorithm.Update()
|
||||
|
||||
@property
|
||||
def output(self):
|
||||
"""Returns a single data object or a tuple of data objects
|
||||
if there are multiple outputs."""
|
||||
if self.algorithm.GetNumberOfOutputPorts() == 1:
|
||||
return self.algorithm.GetOutputDataObject(0)
|
||||
else:
|
||||
outputs = []
|
||||
nOutputs = self.algorithm.GetNumberOfOutputPorts()
|
||||
for i in range(nOutputs):
|
||||
outputs.append(self.algorithm.GetOutputDataObject(i))
|
||||
return tuple(outputs)
|
||||
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Utility module to make it easier to create new keys.
|
||||
"""
|
||||
from vtkmodules.vtkCommonCore import vtkInformationDataObjectKey as DataaObjectKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationDoubleKey as DoubleKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationDoubleVectorKey as DoubleVectorKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationIdTypeKey as IdTypeKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationInformationKey as InformationKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationInformationVectorKey as InformationVectorKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationIntegerKey as IntegerKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationIntegerVectorKey as IntegerVectorKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationKeyVectorKey as KeyVectorKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationObjectBaseKey as ObjectBaseKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationObjectBaseVectorKey as ObjectBaseVectorKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationRequestKey as RequestKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationStringKey as StringKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationStringVectorKey as StringVectorKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationUnsignedLongKey as UnsignedLongKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationVariantKey as VariantKey
|
||||
from vtkmodules.vtkCommonCore import vtkInformationVariantVectorKey as VariantVectorKey
|
||||
from vtkmodules.vtkCommonExecutionModel import vtkInformationDataObjectMetaDataKey as DataObjectMetaDataKey
|
||||
from vtkmodules.vtkCommonExecutionModel import vtkInformationExecutivePortKey as ExecutivePortKey
|
||||
from vtkmodules.vtkCommonExecutionModel import vtkInformationExecutivePortVectorKey as ExecutivePortVectorKey
|
||||
from vtkmodules.vtkCommonExecutionModel import vtkInformationIntegerRequestKey as IntegerRequestKey
|
||||
|
||||
def MakeKey(key_type, name, location, *args):
|
||||
"""Given a key type, make a new key of given name
|
||||
and location."""
|
||||
return key_type.MakeKey(name, location, *args)
|
||||
@@ -0,0 +1,133 @@
|
||||
"""Miscellaneous functions and classes that don't fit into specific
|
||||
categories."""
|
||||
|
||||
import sys, os
|
||||
from functools import wraps
|
||||
import warnings
|
||||
|
||||
def deprecated(version, message):
|
||||
"""
|
||||
Decorator to mark functions as deprecated.
|
||||
When the decorated function is called, a DeprecationWarning is issued with the provided message.
|
||||
|
||||
Example
|
||||
-------
|
||||
>>> @deprecated(version=1.2, message="Use 'new_function' instead.")
|
||||
... def old_function():
|
||||
... pass
|
||||
|
||||
>>> old_function()
|
||||
DeprecationWarning: Function 'old_function' is deprecated since 1.2. Use 'new_function' instead.
|
||||
|
||||
Note you can filter warning messages, see: https://docs.python.org/3/library/warnings.html#describing-warning-filters
|
||||
"""
|
||||
def decorator(func):
|
||||
warn = f"Function '{func.__name__}' is deprecated since version {version}. " + message
|
||||
@wraps(func)
|
||||
def wrapped(*args, **kwargs):
|
||||
warnings.warn(warn, DeprecationWarning)
|
||||
return func(*args, **kwargs)
|
||||
return wrapped
|
||||
return decorator
|
||||
|
||||
def calldata_type(type):
|
||||
"""set_call_data_type(type) -- convenience decorator to easily set the CallDataType attribute
|
||||
for python function used as observer callback.
|
||||
For example:
|
||||
|
||||
import vtkmodules.util.calldata_type
|
||||
import vtkmodules.util.vtkConstants
|
||||
import vtkmodules.vtkCommonCore import vtkCommand, vtkLookupTable
|
||||
|
||||
@calldata_type(vtkConstants.VTK_STRING)
|
||||
def onError(caller, event, calldata):
|
||||
print("caller: %s - event: %s - msg: %s" % (caller.GetClassName(), event, calldata))
|
||||
|
||||
lt = vtkLookupTable()
|
||||
lt.AddObserver(vtkCommand.ErrorEvent, onError)
|
||||
lt.SetTableRange(2,1)
|
||||
"""
|
||||
from vtkmodules import vtkCommonCore
|
||||
supported_call_data_types = ['string0', vtkCommonCore.VTK_STRING,
|
||||
vtkCommonCore.VTK_OBJECT, vtkCommonCore.VTK_INT,
|
||||
vtkCommonCore.VTK_LONG, vtkCommonCore.VTK_DOUBLE, vtkCommonCore.VTK_FLOAT]
|
||||
|
||||
if type not in supported_call_data_types:
|
||||
raise TypeError("'%s' is not a supported VTK call data type. Supported types are: %s" % (type, supported_call_data_types))
|
||||
|
||||
def wrap(f):
|
||||
f.CallDataType = type
|
||||
return f
|
||||
|
||||
return wrap
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# the following functions are for the vtk regression testing and examples
|
||||
|
||||
def vtkGetDataRoot():
|
||||
"""vtkGetDataRoot() -- return vtk example data directory"""
|
||||
dataRoot = None
|
||||
for i, argv in enumerate(sys.argv):
|
||||
if argv == '-D' and i+1 < len(sys.argv):
|
||||
dataRoot = sys.argv[i+1]
|
||||
|
||||
if dataRoot is None:
|
||||
dataRoot = os.environ.get('VTK_DATA_ROOT', '../../../../VTKData')
|
||||
|
||||
return dataRoot
|
||||
|
||||
def vtkGetTempDir():
|
||||
"""vtkGetTempDir() -- return vtk testing temp dir"""
|
||||
tempDir = None
|
||||
for i, argv in enumerate(sys.argv):
|
||||
if argv == '-T' and i+1 < len(sys.argv):
|
||||
tempDir = sys.argv[i+1]
|
||||
|
||||
if tempDir is None:
|
||||
tempDir = '.'
|
||||
|
||||
return tempDir
|
||||
|
||||
def vtkRegressionTestImage(renWin):
|
||||
"""vtkRegressionTestImage(renWin) -- produce regression image for window
|
||||
|
||||
This function writes out a regression .png file for a vtkWindow.
|
||||
Does anyone involved in testing care to elaborate?
|
||||
"""
|
||||
from vtkmodules.vtkRenderingCore import vtkWindowToImageFilter
|
||||
from vtkmodules.vtkIOImage import vtkPNGReader
|
||||
from vtkmodules.vtkImagingCore import vtkImageDifference
|
||||
|
||||
fname = None
|
||||
for i, argv in enumerate(sys.argv):
|
||||
if argv == '-V' and i+1 < len(sys.argv):
|
||||
fname = os.path.join(vtkGetDataRoot(), sys.argv[i+1])
|
||||
|
||||
if fname is None:
|
||||
return 2
|
||||
|
||||
else:
|
||||
rt_w2if = vtkWindowToImageFilter()
|
||||
rt_w2if.SetInput(renWin)
|
||||
|
||||
if not os.path.isfile(fname):
|
||||
rt_pngw = vtkPNGWriter()
|
||||
rt_pngw.SetFileName(fname)
|
||||
rt_pngw.SetInputConnection(rt_w2if.GetOutputPort())
|
||||
rt_pngw.Write()
|
||||
rt_pngw = None
|
||||
|
||||
rt_png = vtkPNGReader()
|
||||
rt_png.SetFileName(fname)
|
||||
|
||||
rt_id = vtkImageDifference()
|
||||
rt_id.SetInputConnection(rt_w2if.GetOutputPort())
|
||||
rt_id.SetImageConnection(rt_png.GetOutputPort())
|
||||
rt_id.Update()
|
||||
|
||||
if rt_id.GetThresholdedError() <= 10:
|
||||
return 1
|
||||
else:
|
||||
sys.stderr.write('Failed image test: %f\n'
|
||||
% rt_id.GetThresholdedError())
|
||||
return 0
|
||||
@@ -0,0 +1,252 @@
|
||||
"""This module adds support to easily import and export NumPy
|
||||
(http://numpy.scipy.org) arrays into/out of VTK arrays. The code is
|
||||
loosely based on TVTK (https://svn.enthought.com/enthought/wiki/TVTK).
|
||||
|
||||
This code depends on an addition to the VTK data arrays made by Berk
|
||||
Geveci to make it support Python's buffer protocol (on Feb. 15, 2008).
|
||||
|
||||
The main functionality of this module is provided by the two functions:
|
||||
numpy_to_vtk,
|
||||
vtk_to_numpy.
|
||||
|
||||
|
||||
Caveats:
|
||||
--------
|
||||
|
||||
- Bit arrays in general do not have a numpy equivalent and are not
|
||||
supported. Char arrays are also not easy to handle and might not
|
||||
work as you expect. Patches welcome.
|
||||
|
||||
- You need to make sure you hold a reference to a Numpy array you want
|
||||
to import into VTK. If not you'll get a segfault (in the best case).
|
||||
The same holds in reverse when you convert a VTK array to a numpy
|
||||
array -- don't delete the VTK array.
|
||||
|
||||
|
||||
Created by Prabhu Ramachandran in Feb. 2008.
|
||||
"""
|
||||
|
||||
from . import vtkConstants
|
||||
from vtkmodules.vtkCommonCore import vtkDataArray, vtkIdTypeArray, vtkLongArray
|
||||
import numpy
|
||||
|
||||
# Useful constants for VTK arrays.
|
||||
VTK_ID_TYPE_SIZE = vtkIdTypeArray().GetDataTypeSize()
|
||||
if VTK_ID_TYPE_SIZE == 4:
|
||||
ID_TYPE_CODE = numpy.int32
|
||||
elif VTK_ID_TYPE_SIZE == 8:
|
||||
ID_TYPE_CODE = numpy.int64
|
||||
|
||||
VTK_LONG_TYPE_SIZE = vtkLongArray().GetDataTypeSize()
|
||||
if VTK_LONG_TYPE_SIZE == 4:
|
||||
LONG_TYPE_CODE = numpy.int32
|
||||
ULONG_TYPE_CODE = numpy.uint32
|
||||
elif VTK_LONG_TYPE_SIZE == 8:
|
||||
LONG_TYPE_CODE = numpy.int64
|
||||
ULONG_TYPE_CODE = numpy.uint64
|
||||
|
||||
|
||||
def get_vtk_array_type(numpy_array_type):
|
||||
"""Returns a VTK typecode given a numpy array."""
|
||||
# This is a Mapping from numpy array types to VTK array types.
|
||||
_np_vtk = {numpy.uint8:vtkConstants.VTK_UNSIGNED_CHAR,
|
||||
numpy.uint16:vtkConstants.VTK_UNSIGNED_SHORT,
|
||||
numpy.uint32:vtkConstants.VTK_UNSIGNED_INT,
|
||||
numpy.uint64:vtkConstants.VTK_UNSIGNED_LONG_LONG,
|
||||
numpy.int8:vtkConstants.VTK_SIGNED_CHAR,
|
||||
numpy.int16:vtkConstants.VTK_SHORT,
|
||||
numpy.int32:vtkConstants.VTK_INT,
|
||||
numpy.int64:vtkConstants.VTK_LONG_LONG,
|
||||
numpy.float32:vtkConstants.VTK_FLOAT,
|
||||
numpy.float64:vtkConstants.VTK_DOUBLE,
|
||||
numpy.complex64:vtkConstants.VTK_FLOAT,
|
||||
numpy.complex128:vtkConstants.VTK_DOUBLE}
|
||||
for key, vtk_type in _np_vtk.items():
|
||||
if numpy_array_type == key or \
|
||||
numpy.issubdtype(numpy_array_type, key) or \
|
||||
numpy_array_type == numpy.dtype(key):
|
||||
return vtk_type
|
||||
raise TypeError(
|
||||
'Could not find a suitable VTK type for %s' % (str(numpy_array_type)))
|
||||
|
||||
def get_vtk_to_numpy_typemap():
|
||||
"""Returns the VTK array type to numpy array type mapping."""
|
||||
_vtk_np = {vtkConstants.VTK_BIT:numpy.uint8,
|
||||
vtkConstants.VTK_CHAR:numpy.int8,
|
||||
vtkConstants.VTK_SIGNED_CHAR:numpy.int8,
|
||||
vtkConstants.VTK_UNSIGNED_CHAR:numpy.uint8,
|
||||
vtkConstants.VTK_SHORT:numpy.int16,
|
||||
vtkConstants.VTK_UNSIGNED_SHORT:numpy.uint16,
|
||||
vtkConstants.VTK_INT:numpy.int32,
|
||||
vtkConstants.VTK_UNSIGNED_INT:numpy.uint32,
|
||||
vtkConstants.VTK_LONG:LONG_TYPE_CODE,
|
||||
vtkConstants.VTK_LONG_LONG:numpy.int64,
|
||||
vtkConstants.VTK_UNSIGNED_LONG:ULONG_TYPE_CODE,
|
||||
vtkConstants.VTK_UNSIGNED_LONG_LONG:numpy.uint64,
|
||||
vtkConstants.VTK_ID_TYPE:ID_TYPE_CODE,
|
||||
vtkConstants.VTK_FLOAT:numpy.float32,
|
||||
vtkConstants.VTK_DOUBLE:numpy.float64}
|
||||
return _vtk_np
|
||||
|
||||
|
||||
def get_numpy_array_type(vtk_array_type):
|
||||
"""Returns a numpy array typecode given a VTK array type."""
|
||||
return get_vtk_to_numpy_typemap()[vtk_array_type]
|
||||
|
||||
|
||||
def create_vtk_array(vtk_arr_type):
|
||||
"""Internal function used to create a VTK data array from another
|
||||
VTK array given the VTK array type.
|
||||
"""
|
||||
return vtkDataArray.CreateDataArray(vtk_arr_type)
|
||||
|
||||
|
||||
def numpy_to_vtk(num_array, deep=0, array_type=None):
|
||||
"""Converts a real numpy Array to a VTK array object.
|
||||
|
||||
This function only works for real arrays.
|
||||
Complex arrays are NOT handled. It also works for multi-component
|
||||
arrays. However, only 1, and 2 dimensional arrays are supported.
|
||||
This function is very efficient, so large arrays should not be a
|
||||
problem.
|
||||
|
||||
If the second argument is set to 1, the array is deep-copied from
|
||||
from numpy. This is not as efficient as the default behavior
|
||||
(shallow copy) and uses more memory but detaches the two arrays
|
||||
such that the numpy array can be released.
|
||||
|
||||
WARNING: You must maintain a reference to the passed numpy array, if
|
||||
the numpy data is gc'd and VTK will point to garbage which will in
|
||||
the best case give you a segfault.
|
||||
|
||||
Parameters:
|
||||
|
||||
num_array
|
||||
a 1D or 2D, real numpy array.
|
||||
|
||||
"""
|
||||
|
||||
z = numpy.asarray(num_array)
|
||||
if not z.flags.contiguous:
|
||||
z = numpy.ascontiguousarray(z)
|
||||
|
||||
shape = z.shape
|
||||
assert z.flags.contiguous, 'Only contiguous arrays are supported.'
|
||||
assert len(shape) < 3, \
|
||||
"Only arrays of dimensionality 2 or lower are allowed!"
|
||||
assert not numpy.issubdtype(z.dtype, numpy.dtype(complex).type), \
|
||||
"Complex numpy arrays cannot be converted to vtk arrays."\
|
||||
"Use real() or imag() to get a component of the array before"\
|
||||
" passing it to vtk."
|
||||
|
||||
# First create an array of the right type by using the typecode.
|
||||
if array_type:
|
||||
vtk_typecode = array_type
|
||||
else:
|
||||
vtk_typecode = get_vtk_array_type(z.dtype)
|
||||
result_array = create_vtk_array(vtk_typecode)
|
||||
|
||||
# Fixup shape in case its empty or scalar.
|
||||
try:
|
||||
testVar = shape[0]
|
||||
except:
|
||||
shape = (0,)
|
||||
|
||||
# Find the shape and set number of components.
|
||||
if len(shape) == 1:
|
||||
result_array.SetNumberOfComponents(1)
|
||||
else:
|
||||
result_array.SetNumberOfComponents(shape[1])
|
||||
|
||||
# We don't need to call result_array.SetNumberOfTuples(shape[0])
|
||||
# because we will use result_array.SetVoidPointer
|
||||
# which takes care of setting the NumberOfTuples
|
||||
# Calling SetNumberOfTuples will result in a memory allocation
|
||||
# that will be deleted on SetVoidPointer.
|
||||
|
||||
# Ravel the array appropriately.
|
||||
arr_dtype = get_numpy_array_type(vtk_typecode)
|
||||
if numpy.issubdtype(z.dtype, arr_dtype) or \
|
||||
z.dtype == numpy.dtype(arr_dtype):
|
||||
z_flat = numpy.ravel(z)
|
||||
else:
|
||||
z_flat = numpy.ravel(z).astype(arr_dtype)
|
||||
# z_flat is now a standalone object with no references from the caller.
|
||||
# As such, it will drop out of this scope and cause memory issues if we
|
||||
# do not deep copy its data.
|
||||
deep = 1
|
||||
|
||||
# Point the VTK array to the numpy data. The last argument (1)
|
||||
# tells the array not to deallocate.
|
||||
result_array.SetVoidArray(z_flat, len(z_flat), 1)
|
||||
if deep:
|
||||
copy = result_array.NewInstance()
|
||||
copy.DeepCopy(result_array)
|
||||
result_array = copy
|
||||
else:
|
||||
result_array._numpy_reference = z
|
||||
return result_array
|
||||
|
||||
def numpy_to_vtkIdTypeArray(num_array, deep=0):
|
||||
isize = vtkIdTypeArray().GetDataTypeSize()
|
||||
dtype = num_array.dtype
|
||||
if isize == 4:
|
||||
if dtype != numpy.int32:
|
||||
raise ValueError(
|
||||
'Expecting a numpy.int32 array, got %s instead.' % (str(dtype)))
|
||||
else:
|
||||
if dtype != numpy.int64:
|
||||
raise ValueError(
|
||||
'Expecting a numpy.int64 array, got %s instead.' % (str(dtype)))
|
||||
|
||||
return numpy_to_vtk(num_array, deep, vtkConstants.VTK_ID_TYPE)
|
||||
|
||||
def vtk_to_numpy(vtk_array):
|
||||
"""Converts a VTK data array to a numpy array.
|
||||
|
||||
Given a subclass of vtkDataArray, this function returns an
|
||||
appropriate numpy array containing the same data -- it actually
|
||||
points to the same data.
|
||||
|
||||
Parameters
|
||||
|
||||
vtk_array
|
||||
The VTK data array to be converted.
|
||||
|
||||
"""
|
||||
typ = vtk_array.GetDataType()
|
||||
assert typ in get_vtk_to_numpy_typemap().keys(), \
|
||||
"Unsupported array type %s"%typ
|
||||
|
||||
shape = vtk_array.GetNumberOfTuples(), \
|
||||
vtk_array.GetNumberOfComponents()
|
||||
|
||||
# Get the data via the buffer interface
|
||||
dtype = get_numpy_array_type(typ)
|
||||
try:
|
||||
if typ != vtkConstants.VTK_BIT:
|
||||
result = numpy.frombuffer(vtk_array, dtype=dtype)
|
||||
else:
|
||||
result = numpy.unpackbits(vtk_array, count=shape[0])
|
||||
except ValueError:
|
||||
# http://mail.scipy.org/pipermail/numpy-tickets/2011-August/005859.html
|
||||
# numpy 1.5.1 (and maybe earlier) has a bug where if frombuffer is
|
||||
# called with an empty buffer, it throws ValueError exception. This
|
||||
# handles that issue.
|
||||
if shape[0] == 0:
|
||||
# create an empty array with the given shape.
|
||||
result = numpy.empty(shape, dtype=dtype)
|
||||
else:
|
||||
raise
|
||||
if shape[1] == 1:
|
||||
shape = (shape[0], )
|
||||
try:
|
||||
result.shape = shape
|
||||
except ValueError:
|
||||
if shape[0] == 0:
|
||||
# Refer to https://github.com/numpy/numpy/issues/2536 .
|
||||
# For empty array, reshape fails. Create the empty array explicitly
|
||||
# if that happens.
|
||||
result = numpy.empty(shape, dtype=dtype)
|
||||
else: raise
|
||||
return result
|
||||
@@ -0,0 +1,108 @@
|
||||
"""This module generates support for pickling vtkDataObjects from python.
|
||||
It needs to be imported specifically in order to work:
|
||||
|
||||
>>> import vtkmodules.util.pickle_support
|
||||
|
||||
Once imported however, the pickling of data objects is very straightforward. Here is an
|
||||
example using poly data:
|
||||
|
||||
>>> sphereSrc = vtkSphereSource()
|
||||
>>> sphereSrc.Update()
|
||||
>>> pickled = pickle.dumps(sphereSrc.GetOutput())
|
||||
>>> unpickled = pickle.loads(pickled)
|
||||
>>> print(unpickled)
|
||||
*description of sphere data set*
|
||||
|
||||
The underlying serialization of the vtkDatObjects is based on the marshaling capabilities
|
||||
found in vtkCommunicator. Importing this module adds entries for the most common data
|
||||
objects in the global dispatch table used by pickle. NumPy is required as well since the
|
||||
-serialized data object gets pickled as a numpy array.
|
||||
"""
|
||||
|
||||
try:
|
||||
import copyreg, pickle, numpy
|
||||
except ImportError:
|
||||
raise ImportError("This module depends on the pickle, copyreg, and numpy modules.\
|
||||
Please make sure that it is installed properly.")
|
||||
|
||||
from ..vtkParallelCore import vtkCommunicator
|
||||
from ..vtkCommonCore import vtkCharArray
|
||||
from .. import vtkCommonDataModel
|
||||
|
||||
def unserialize_VTK_data_object(state):
|
||||
"""Takes a state dictionary with entries:
|
||||
- Type : a string with the class name for the data object
|
||||
- Serialized : a numpy array with the serialized data object
|
||||
|
||||
and transforms it into a data object.
|
||||
"""
|
||||
|
||||
if ("Type" not in state.keys()) or ("Serialized" not in state.keys()):
|
||||
raise RuntimeError("State dictionary passed to unpickle does not have Type and/or\
|
||||
Serialized keys.")
|
||||
|
||||
new_data_object = None
|
||||
DataSetClass = None
|
||||
try:
|
||||
DataSetClass = getattr(vtkCommonDataModel, state["Type"])
|
||||
except:
|
||||
raise TypeError("Could not find type " + type_string + " in vtkCommonDataModel module")
|
||||
serialized_data = state["Serialized"]
|
||||
new_data_object = DataSetClass()
|
||||
char_array = vtkCharArray()
|
||||
char_array.SetVoidArray(serialized_data, memoryview(serialized_data).nbytes, 1)
|
||||
if vtkCommunicator.UnMarshalDataObject(char_array, new_data_object) == 0:
|
||||
raise RuntimeError("Marshaling data object failed")
|
||||
return new_data_object
|
||||
|
||||
def serialize_VTK_data_object(data_object):
|
||||
"""Returns a tuple with a reference to the unpickling function and a state dictionary
|
||||
with entries:
|
||||
- Type : a string with the class name for the data object
|
||||
- Serialized : a numpy array with the serialized data object
|
||||
|
||||
This is exactly the state dictionary that unserialize_VTK_data_object expects.
|
||||
"""
|
||||
|
||||
if not data_object.IsA("vtkDataObject"):
|
||||
raise TypeError("Object passed to pickling should be a vtkDataObject")
|
||||
data_object_type = data_object.GetClassName()
|
||||
char_array = vtkCharArray()
|
||||
if vtkCommunicator.MarshalDataObject(data_object, char_array) == 0:
|
||||
raise RuntimeError("UnMarshaling data object failed")
|
||||
return unserialize_VTK_data_object, (
|
||||
{ "Type" : data_object_type,
|
||||
"Serialized" : numpy.frombuffer(char_array, numpy.int8, char_array.GetNumberOfValues()) },)
|
||||
|
||||
|
||||
# Fill in global dispatch table for most vtkDataObject types
|
||||
copyreg.pickle(vtkCommonDataModel.vtkDataSet, serialize_VTK_data_object)
|
||||
|
||||
copyreg.pickle(vtkCommonDataModel.vtkPolyData, serialize_VTK_data_object)
|
||||
copyreg.pickle(vtkCommonDataModel.vtkUnstructuredGrid, serialize_VTK_data_object)
|
||||
|
||||
copyreg.pickle(vtkCommonDataModel.vtkImageData, serialize_VTK_data_object)
|
||||
copyreg.pickle(vtkCommonDataModel.vtkRectilinearGrid, serialize_VTK_data_object)
|
||||
copyreg.pickle(vtkCommonDataModel.vtkStructuredGrid, serialize_VTK_data_object)
|
||||
copyreg.pickle(vtkCommonDataModel.vtkExplicitStructuredGrid, serialize_VTK_data_object)
|
||||
copyreg.pickle(vtkCommonDataModel.vtkStructuredPoints, serialize_VTK_data_object)
|
||||
|
||||
copyreg.pickle(vtkCommonDataModel.vtkUniformGridAMR, serialize_VTK_data_object)
|
||||
copyreg.pickle(vtkCommonDataModel.vtkOverlappingAMR, serialize_VTK_data_object)
|
||||
copyreg.pickle(vtkCommonDataModel.vtkHierarchicalBoxDataSet, serialize_VTK_data_object) # VTK_DEPRECATED_IN_9_5_0
|
||||
copyreg.pickle(vtkCommonDataModel.vtkNonOverlappingAMR, serialize_VTK_data_object)
|
||||
|
||||
copyreg.pickle(vtkCommonDataModel.vtkTable, serialize_VTK_data_object)
|
||||
|
||||
copyreg.pickle(vtkCommonDataModel.vtkTree, serialize_VTK_data_object)
|
||||
|
||||
copyreg.pickle(vtkCommonDataModel.vtkCompositeDataSet, serialize_VTK_data_object)
|
||||
copyreg.pickle(vtkCommonDataModel.vtkDataObjectTree, serialize_VTK_data_object)
|
||||
copyreg.pickle(vtkCommonDataModel.vtkMultiBlockDataSet, serialize_VTK_data_object)
|
||||
copyreg.pickle(vtkCommonDataModel.vtkPartitionedDataSet, serialize_VTK_data_object)
|
||||
copyreg.pickle(vtkCommonDataModel.vtkPartitionedDataSetCollection, serialize_VTK_data_object)
|
||||
copyreg.pickle(vtkCommonDataModel.vtkMultiPieceDataSet, serialize_VTK_data_object)
|
||||
|
||||
copyreg.pickle(vtkCommonDataModel.vtkDirectedGraph, serialize_VTK_data_object)
|
||||
copyreg.pickle(vtkCommonDataModel.vtkUndirectedGraph, serialize_VTK_data_object)
|
||||
copyreg.pickle(vtkCommonDataModel.vtkMolecule, serialize_VTK_data_object)
|
||||
@@ -0,0 +1,220 @@
|
||||
from vtkmodules.vtkCommonCore import vtkInformation
|
||||
from vtkmodules.vtkCommonDataModel import vtkDataObject
|
||||
from vtkmodules.vtkCommonExecutionModel import vtkAlgorithm
|
||||
from vtkmodules.vtkCommonExecutionModel import vtkDemandDrivenPipeline
|
||||
from vtkmodules.vtkCommonExecutionModel import vtkStreamingDemandDrivenPipeline
|
||||
from vtkmodules.vtkFiltersPython import vtkPythonAlgorithm
|
||||
|
||||
class VTKAlgorithm(object):
|
||||
"""This is a superclass which can be derived to implement
|
||||
Python classes that work with vtkPythonAlgorithm. It implements
|
||||
Initialize(), ProcessRequest(), FillInputPortInformation() and
|
||||
FillOutputPortInformation().
|
||||
|
||||
Initialize() sets the input and output ports based on data
|
||||
members.
|
||||
|
||||
ProcessRequest() calls RequestXXX() methods to implement
|
||||
various pipeline passes.
|
||||
|
||||
FillInputPortInformation() and FillOutputPortInformation() set
|
||||
the input and output types based on data members.
|
||||
"""
|
||||
|
||||
def __init__(self, nInputPorts=1, inputType='vtkDataSet',
|
||||
nOutputPorts=1, outputType='vtkPolyData'):
|
||||
"""Sets up default NumberOfInputPorts, NumberOfOutputPorts,
|
||||
InputType and OutputType that are used by various initialization
|
||||
methods."""
|
||||
|
||||
self.NumberOfInputPorts = nInputPorts
|
||||
self.NumberOfOutputPorts = nOutputPorts
|
||||
self.InputType = inputType
|
||||
self.OutputType = outputType
|
||||
|
||||
def Initialize(self, vtkself):
|
||||
"""Sets up number of input and output ports based on
|
||||
NumberOfInputPorts and NumberOfOutputPorts."""
|
||||
|
||||
vtkself.SetNumberOfInputPorts(self.NumberOfInputPorts)
|
||||
vtkself.SetNumberOfOutputPorts(self.NumberOfOutputPorts)
|
||||
|
||||
def GetInputData(self, inInfo, i, j):
|
||||
"""Convenience method that returns an input data object
|
||||
given a vector of information objects and two indices."""
|
||||
|
||||
return inInfo[i].GetInformationObject(j).Get(vtkDataObject.DATA_OBJECT())
|
||||
|
||||
def GetOutputData(self, outInfo, i):
|
||||
"""Convenience method that returns an output data object
|
||||
given an information object and an index."""
|
||||
return outInfo.GetInformationObject(i).Get(vtkDataObject.DATA_OBJECT())
|
||||
|
||||
def RequestDataObject(self, vtkself, request, inInfo, outInfo):
|
||||
"""Overwritten by subclass to manage data object creation.
|
||||
There is not need to overwrite this class if the output can
|
||||
be created based on the OutputType data member."""
|
||||
return 1
|
||||
|
||||
def RequestInformation(self, vtkself, request, inInfo, outInfo):
|
||||
"""Overwritten by subclass to provide meta-data to downstream
|
||||
pipeline."""
|
||||
return 1
|
||||
|
||||
def RequestUpdateExtent(self, vtkself, request, inInfo, outInfo):
|
||||
"""Overwritten by subclass to modify data request going
|
||||
to upstream pipeline."""
|
||||
return 1
|
||||
|
||||
def RequestData(self, vtkself, request, inInfo, outInfo):
|
||||
"""Overwritten by subclass to execute the algorithm."""
|
||||
raise NotImplementedError('RequestData must be implemented')
|
||||
|
||||
def ProcessRequest(self, vtkself, request, inInfo, outInfo):
|
||||
"""Splits a request to RequestXXX() methods."""
|
||||
if request.Has(vtkDemandDrivenPipeline.REQUEST_DATA_OBJECT()):
|
||||
return self.RequestDataObject(vtkself, request, inInfo, outInfo)
|
||||
elif request.Has(vtkDemandDrivenPipeline.REQUEST_INFORMATION()):
|
||||
return self.RequestInformation(vtkself, request, inInfo, outInfo)
|
||||
elif request.Has(vtkStreamingDemandDrivenPipeline.REQUEST_UPDATE_EXTENT()):
|
||||
return self.RequestUpdateExtent(vtkself, request, inInfo, outInfo)
|
||||
elif request.Has(vtkDemandDrivenPipeline.REQUEST_DATA()):
|
||||
return self.RequestData(vtkself, request, inInfo, outInfo)
|
||||
|
||||
return 1
|
||||
|
||||
def FillInputPortInformation(self, vtkself, port, info):
|
||||
"""Sets the required input type to InputType."""
|
||||
info.Set(vtkAlgorithm.INPUT_REQUIRED_DATA_TYPE(), self.InputType)
|
||||
return 1
|
||||
|
||||
def FillOutputPortInformation(self, vtkself, port, info):
|
||||
"""Sets the default output type to OutputType."""
|
||||
info.Set(vtkDataObject.DATA_TYPE_NAME(), self.OutputType)
|
||||
return 1
|
||||
|
||||
class VTKPythonAlgorithmBase(vtkPythonAlgorithm):
|
||||
"""This is a superclass which can be derived to implement
|
||||
Python classes that act as VTK algorithms in a VTK pipeline.
|
||||
It implements ProcessRequest(), FillInputPortInformation() and
|
||||
FillOutputPortInformation().
|
||||
|
||||
ProcessRequest() calls RequestXXX() methods to implement
|
||||
various pipeline passes.
|
||||
|
||||
FillInputPortInformation() and FillOutputPortInformation() set
|
||||
the input and output types based on data members.
|
||||
|
||||
Common use is something like this:
|
||||
|
||||
class HDF5Source(VTKPythonAlgorithmBase):
|
||||
def __init__(self):
|
||||
VTKPythonAlgorithmBase.__init__(self,
|
||||
nInputPorts=0,
|
||||
nOutputPorts=1, outputType='vtkImageData')
|
||||
|
||||
def RequestInformation(self, request, inInfo, outInfo):
|
||||
f = h5py.File("foo.h5", 'r')
|
||||
dims = f['RTData'].shape[::-1]
|
||||
info = outInfo.GetInformationObject(0)
|
||||
info.Set(vtkmodules.vtkCommonExecutionModel.vtkStreamingDemandDrivenPipeline.WHOLE_EXTENT(),
|
||||
(0, dims[0]-1, 0, dims[1]-1, 0, dims[2]-1), 6)
|
||||
return 1
|
||||
|
||||
def RequestData(self, request, inInfo, outInfo):
|
||||
f = h5py.File("foo.h5", 'r')
|
||||
data = f['RTData'][:]
|
||||
output = dsa.WrapDataObject(vtkmodules.vtkCommonDataModel.vtkImageData.GetData(outInfo))
|
||||
output.SetDimensions(data.shape)
|
||||
output.PointData.append(data.flatten(), 'RTData')
|
||||
output.PointData.SetActiveScalars('RTData')
|
||||
return 1
|
||||
|
||||
alg = HDF5Source()
|
||||
|
||||
cf = vtkmodules.vtkFiltersCore.vtkContourFilter()
|
||||
cf.SetInputConnection(alg.GetOutputPort())
|
||||
cf.Update()
|
||||
"""
|
||||
|
||||
class InternalAlgorithm(object):
|
||||
"Internal class. Do not use."
|
||||
def Initialize(self, vtkself):
|
||||
pass
|
||||
|
||||
def FillInputPortInformation(self, vtkself, port, info):
|
||||
return vtkself.FillInputPortInformation(port, info)
|
||||
|
||||
def FillOutputPortInformation(self, vtkself, port, info):
|
||||
return vtkself.FillOutputPortInformation(port, info)
|
||||
|
||||
def ProcessRequest(self, vtkself, request, inInfo, outInfo):
|
||||
return vtkself.ProcessRequest(request, inInfo, outInfo)
|
||||
|
||||
def __init__(self, nInputPorts=1, inputType='vtkDataSet',
|
||||
nOutputPorts=1, outputType='vtkPolyData'):
|
||||
"""Sets up default NumberOfInputPorts, NumberOfOutputPorts,
|
||||
InputType and OutputType that are used by various methods.
|
||||
Make sure to call this method from any subclass' __init__"""
|
||||
|
||||
self.SetPythonObject(VTKPythonAlgorithmBase.InternalAlgorithm())
|
||||
|
||||
self.SetNumberOfInputPorts(nInputPorts)
|
||||
self.SetNumberOfOutputPorts(nOutputPorts)
|
||||
|
||||
self.InputType = inputType
|
||||
self.OutputType = outputType
|
||||
|
||||
def GetInputData(self, inInfo, i, j):
|
||||
"""Convenience method that returns an input data object
|
||||
given a vector of information objects and two indices."""
|
||||
|
||||
return inInfo[i].GetInformationObject(j).Get(vtkDataObject.DATA_OBJECT())
|
||||
|
||||
def GetOutputData(self, outInfo, i):
|
||||
"""Convenience method that returns an output data object
|
||||
given an information object and an index."""
|
||||
return outInfo.GetInformationObject(i).Get(vtkDataObject.DATA_OBJECT())
|
||||
|
||||
def FillInputPortInformation(self, port, info):
|
||||
"""Sets the required input type to InputType."""
|
||||
info.Set(vtkAlgorithm.INPUT_REQUIRED_DATA_TYPE(), self.InputType)
|
||||
return 1
|
||||
|
||||
def FillOutputPortInformation(self, port, info):
|
||||
"""Sets the default output type to OutputType."""
|
||||
info.Set(vtkDataObject.DATA_TYPE_NAME(), self.OutputType)
|
||||
return 1
|
||||
|
||||
def ProcessRequest(self, request, inInfo, outInfo):
|
||||
"""Splits a request to RequestXXX() methods."""
|
||||
if request.Has(vtkDemandDrivenPipeline.REQUEST_DATA_OBJECT()):
|
||||
return self.RequestDataObject(request, inInfo, outInfo)
|
||||
elif request.Has(vtkDemandDrivenPipeline.REQUEST_INFORMATION()):
|
||||
return self.RequestInformation(request, inInfo, outInfo)
|
||||
elif request.Has(vtkStreamingDemandDrivenPipeline.REQUEST_UPDATE_EXTENT()):
|
||||
return self.RequestUpdateExtent(request, inInfo, outInfo)
|
||||
elif request.Has(vtkDemandDrivenPipeline.REQUEST_DATA()):
|
||||
return self.RequestData(request, inInfo, outInfo)
|
||||
|
||||
return 1
|
||||
|
||||
def RequestDataObject(self, request, inInfo, outInfo):
|
||||
"""Overwritten by subclass to manage data object creation.
|
||||
There is not need to overwrite this class if the output can
|
||||
be created based on the OutputType data member."""
|
||||
return 1
|
||||
|
||||
def RequestInformation(self, request, inInfo, outInfo):
|
||||
"""Overwritten by subclass to provide meta-data to downstream
|
||||
pipeline."""
|
||||
return 1
|
||||
|
||||
def RequestUpdateExtent(self, request, inInfo, outInfo):
|
||||
"""Overwritten by subclass to modify data request going
|
||||
to upstream pipeline."""
|
||||
return 1
|
||||
|
||||
def RequestData(self, request, inInfo, outInfo):
|
||||
"""Overwritten by subclass to execute the algorithm."""
|
||||
raise NotImplementedError('RequestData must be implemented')
|
||||
@@ -0,0 +1,201 @@
|
||||
"""
|
||||
This file is obsolete.
|
||||
All the constants are part of the base vtk module.
|
||||
"""
|
||||
|
||||
# Some constants used throughout code
|
||||
|
||||
_VTK_FLOAT_MAX = 1.0e+38
|
||||
_VTK_INT_MAX = 2147483647 # 2^31 - 1
|
||||
|
||||
# These types are returned by GetDataType to indicate pixel type.
|
||||
VTK_VOID = 0
|
||||
VTK_BIT = 1
|
||||
VTK_CHAR = 2
|
||||
VTK_SIGNED_CHAR =15
|
||||
VTK_UNSIGNED_CHAR = 3
|
||||
VTK_SHORT = 4
|
||||
VTK_UNSIGNED_SHORT = 5
|
||||
VTK_INT = 6
|
||||
VTK_UNSIGNED_INT = 7
|
||||
VTK_LONG = 8
|
||||
VTK_UNSIGNED_LONG = 9
|
||||
VTK_FLOAT =10
|
||||
VTK_DOUBLE =11
|
||||
VTK_ID_TYPE =12
|
||||
|
||||
# These types are not currently supported by GetDataType, but are
|
||||
# for completeness.
|
||||
VTK_STRING =13
|
||||
VTK_OPAQUE =14
|
||||
|
||||
VTK_LONG_LONG =16
|
||||
VTK_UNSIGNED_LONG_LONG =17
|
||||
|
||||
# These types are required by vtkVariant and vtkVariantArray
|
||||
VTK_VARIANT =20
|
||||
VTK_OBJECT =21
|
||||
|
||||
# Some constant required for correct template performance
|
||||
VTK_BIT_MIN = 0
|
||||
VTK_BIT_MAX = 1
|
||||
VTK_CHAR_MIN = -128
|
||||
VTK_CHAR_MAX = 127
|
||||
VTK_UNSIGNED_CHAR_MIN = 0
|
||||
VTK_UNSIGNED_CHAR_MAX = 255
|
||||
VTK_SHORT_MIN = -32768
|
||||
VTK_SHORT_MAX = 32767
|
||||
VTK_UNSIGNED_SHORT_MIN = 0
|
||||
VTK_UNSIGNED_SHORT_MAX = 65535
|
||||
VTK_INT_MIN = (-_VTK_INT_MAX-1)
|
||||
VTK_INT_MAX = _VTK_INT_MAX
|
||||
#VTK_UNSIGNED_INT_MIN = 0
|
||||
#VTK_UNSIGNED_INT_MAX = 4294967295
|
||||
VTK_LONG_MIN = (-VTK_INT_MAX-1)
|
||||
VTK_LONG_MAX = VTK_INT_MAX
|
||||
#VTK_UNSIGNED_LONG_MIN = 0
|
||||
#VTK_UNSIGNED_LONG_MAX = 4294967295
|
||||
VTK_FLOAT_MIN = -_VTK_FLOAT_MAX
|
||||
VTK_FLOAT_MAX = _VTK_FLOAT_MAX
|
||||
VTK_DOUBLE_MIN = -1.0e+99
|
||||
VTK_DOUBLE_MAX = 1.0e+99
|
||||
|
||||
# These types are returned to distinguish dataset types
|
||||
VTK_POLY_DATA = 0
|
||||
VTK_STRUCTURED_POINTS = 1
|
||||
VTK_STRUCTURED_GRID = 2
|
||||
VTK_RECTILINEAR_GRID = 3
|
||||
VTK_UNSTRUCTURED_GRID = 4
|
||||
VTK_PIECEWISE_FUNCTION = 5
|
||||
VTK_IMAGE_DATA = 6
|
||||
VTK_DATA_OBJECT = 7
|
||||
VTK_DATA_SET = 8
|
||||
VTK_POINT_SET = 9
|
||||
VTK_UNIFORM_GRID = 10
|
||||
VTK_COMPOSITE_DATA_SET = 11
|
||||
VTK_MULTIGROUP_DATA_SET = 12 # OBSOLETE VTK_DEPRECATED_IN_9_5_0
|
||||
VTK_MULTIBLOCK_DATA_SET = 13
|
||||
VTK_HIERARCHICAL_DATA_SET = 14 # OBSOLETE VTK_DEPRECATED_IN_9_5_0
|
||||
VTK_HIERARCHICAL_BOX_DATA_SET = 15 # OBSOLETE VTK_DEPRECATED_IN_9_5_0
|
||||
VTK_GENERIC_DATA_SET = 16
|
||||
VTK_HYPER_OCTREE = 17 # OBSOLETE VTK_DEPRECATED_IN_9_5_0
|
||||
VTK_TEMPORAL_DATA_SET = 18 # OBSOLETE VTK_DEPRECATED_IN_9_5_0
|
||||
VTK_TABLE = 19
|
||||
VTK_GRAPH = 20
|
||||
VTK_TREE = 21
|
||||
VTK_SELECTION = 22
|
||||
|
||||
# These types define error codes for vtk functions
|
||||
VTK_OK = 1
|
||||
VTK_ERROR = 2
|
||||
|
||||
# These types define different text properties
|
||||
VTK_ARIAL = 0
|
||||
VTK_COURIER = 1
|
||||
VTK_TIMES = 2
|
||||
VTK_UNKNOWN_FONT = 3
|
||||
|
||||
VTK_TEXT_LEFT = 0
|
||||
VTK_TEXT_CENTERED = 1
|
||||
VTK_TEXT_RIGHT = 2
|
||||
|
||||
VTK_TEXT_BOTTOM = 0
|
||||
VTK_TEXT_TOP = 2
|
||||
|
||||
VTK_TEXT_GLOBAL_ANTIALIASING_SOME = 0
|
||||
VTK_TEXT_GLOBAL_ANTIALIASING_NONE = 1
|
||||
VTK_TEXT_GLOBAL_ANTIALIASING_ALL = 2
|
||||
|
||||
VTK_LUMINANCE = 1
|
||||
VTK_LUMINANCE_ALPHA = 2
|
||||
VTK_RGB = 3
|
||||
VTK_RGBA = 4
|
||||
|
||||
VTK_COLOR_MODE_DEFAULT = 0
|
||||
VTK_COLOR_MODE_MAP_SCALARS = 1
|
||||
|
||||
# Constants for InterpolationType
|
||||
VTK_NEAREST_INTERPOLATION = 0
|
||||
VTK_LINEAR_INTERPOLATION = 1
|
||||
|
||||
# For volume rendering
|
||||
VTK_MAX_VRCOMP = 4
|
||||
|
||||
# These types define the 17 linear VTK Cell Types
|
||||
# See Filtering/vtkCellType.h
|
||||
|
||||
# Linear cells
|
||||
VTK_EMPTY_CELL = 0
|
||||
VTK_VERTEX = 1
|
||||
VTK_POLY_VERTEX = 2
|
||||
VTK_LINE = 3
|
||||
VTK_POLY_LINE = 4
|
||||
VTK_TRIANGLE = 5
|
||||
VTK_TRIANGLE_STRIP = 6
|
||||
VTK_POLYGON = 7
|
||||
VTK_PIXEL = 8
|
||||
VTK_QUAD = 9
|
||||
VTK_TETRA = 10
|
||||
VTK_VOXEL = 11
|
||||
VTK_HEXAHEDRON = 12
|
||||
VTK_WEDGE = 13
|
||||
VTK_PYRAMID = 14
|
||||
VTK_PENTAGONAL_PRISM = 15
|
||||
VTK_HEXAGONAL_PRISM = 16
|
||||
|
||||
# Quadratic, isoparametric cells
|
||||
VTK_QUADRATIC_EDGE = 21
|
||||
VTK_QUADRATIC_TRIANGLE = 22
|
||||
VTK_QUADRATIC_QUAD = 23
|
||||
VTK_QUADRATIC_TETRA = 24
|
||||
VTK_QUADRATIC_HEXAHEDRON = 25
|
||||
VTK_QUADRATIC_WEDGE = 26
|
||||
VTK_QUADRATIC_PYRAMID = 27
|
||||
VTK_BIQUADRATIC_QUAD = 28
|
||||
VTK_TRIQUADRATIC_HEXAHEDRON = 29
|
||||
VTK_QUADRATIC_LINEAR_QUAD = 30
|
||||
VTK_QUADRATIC_LINEAR_WEDGE = 31
|
||||
VTK_BIQUADRATIC_QUADRATIC_WEDGE = 32
|
||||
VTK_BIQUADRATIC_QUADRATIC_HEXAHEDRON = 33
|
||||
|
||||
# Special class of cells formed by convex group of points
|
||||
VTK_CONVEX_POINT_SET = 41
|
||||
|
||||
# Higher order cells in parametric form
|
||||
VTK_PARAMETRIC_CURVE = 51
|
||||
VTK_PARAMETRIC_SURFACE = 52
|
||||
VTK_PARAMETRIC_TRI_SURFACE = 53
|
||||
VTK_PARAMETRIC_QUAD_SURFACE = 54
|
||||
VTK_PARAMETRIC_TETRA_REGION = 55
|
||||
VTK_PARAMETRIC_HEX_REGION = 56
|
||||
|
||||
# Higher order cells
|
||||
VTK_HIGHER_ORDER_EDGE = 60
|
||||
VTK_HIGHER_ORDER_TRIANGLE = 61
|
||||
VTK_HIGHER_ORDER_QUAD = 62
|
||||
VTK_HIGHER_ORDER_POLYGON = 63
|
||||
VTK_HIGHER_ORDER_TETRAHEDRON = 64
|
||||
VTK_HIGHER_ORDER_WEDGE = 65
|
||||
VTK_HIGHER_ORDER_PYRAMID = 66
|
||||
VTK_HIGHER_ORDER_HEXAHEDRON = 67
|
||||
|
||||
# A macro to get the name of a type
|
||||
__vtkTypeNameDict = {VTK_VOID:"void",
|
||||
VTK_DOUBLE:"double",
|
||||
VTK_FLOAT:"float",
|
||||
VTK_LONG:"long",
|
||||
VTK_UNSIGNED_LONG:"unsigned long",
|
||||
VTK_INT:"int",
|
||||
VTK_UNSIGNED_INT:"unsigned int",
|
||||
VTK_SHORT:"short",
|
||||
VTK_UNSIGNED_SHORT:"unsigned short",
|
||||
VTK_CHAR:"char",
|
||||
VTK_UNSIGNED_CHAR:"unsigned char",
|
||||
VTK_SIGNED_CHAR:"signed char",
|
||||
VTK_LONG_LONG:"long long",
|
||||
VTK_UNSIGNED_LONG_LONG:"unsigned long long",
|
||||
VTK_ID_TYPE:"vtkIdType",
|
||||
VTK_BIT:"bit"}
|
||||
|
||||
def vtkImageScalarTypeNameMacro(type):
|
||||
return __vtkTypeNameDict[type]
|
||||
@@ -0,0 +1,116 @@
|
||||
"""
|
||||
vtkImageExportToArray - a NumPy front-end to vtkImageExport
|
||||
|
||||
This class converts a VTK image to a numpy array. The output
|
||||
array will always have 3 dimensions (or 4, if the image had
|
||||
multiple scalar components).
|
||||
|
||||
To use this class, you must have numpy installed (http://numpy.scipy.org)
|
||||
|
||||
Methods
|
||||
|
||||
SetInputConnection(vtkAlgorithmOutput) -- connect to VTK image pipeline
|
||||
SetInputData(vtkImageData) -- set an vtkImageData to export
|
||||
GetArray() -- execute pipeline and return a numpy array
|
||||
|
||||
Methods from vtkImageExport
|
||||
|
||||
GetDataExtent()
|
||||
GetDataSpacing()
|
||||
GetDataOrigin()
|
||||
"""
|
||||
|
||||
import numpy
|
||||
import numpy.core.umath as umath
|
||||
|
||||
from vtkmodules.vtkIOImage import vtkImageExport
|
||||
from vtkmodules.vtkCommonExecutionModel import vtkStreamingDemandDrivenPipeline
|
||||
from vtkmodules.vtkCommonCore import VTK_SIGNED_CHAR
|
||||
from vtkmodules.vtkCommonCore import VTK_UNSIGNED_CHAR
|
||||
from vtkmodules.vtkCommonCore import VTK_SHORT
|
||||
from vtkmodules.vtkCommonCore import VTK_UNSIGNED_SHORT
|
||||
from vtkmodules.vtkCommonCore import VTK_INT
|
||||
from vtkmodules.vtkCommonCore import VTK_UNSIGNED_INT
|
||||
from vtkmodules.vtkCommonCore import VTK_LONG
|
||||
from vtkmodules.vtkCommonCore import VTK_UNSIGNED_LONG
|
||||
from vtkmodules.vtkCommonCore import VTK_FLOAT
|
||||
from vtkmodules.vtkCommonCore import VTK_DOUBLE
|
||||
|
||||
|
||||
class vtkImageExportToArray:
|
||||
def __init__(self):
|
||||
self.__export = vtkImageExport()
|
||||
self.__ConvertUnsignedShortToInt = False
|
||||
|
||||
# type dictionary
|
||||
|
||||
__typeDict = { VTK_SIGNED_CHAR:'b',
|
||||
VTK_UNSIGNED_CHAR:'B',
|
||||
VTK_SHORT:'h',
|
||||
VTK_UNSIGNED_SHORT:'H',
|
||||
VTK_INT:'i',
|
||||
VTK_UNSIGNED_INT:'I',
|
||||
VTK_FLOAT:'f',
|
||||
VTK_DOUBLE:'d'}
|
||||
|
||||
__sizeDict = { VTK_SIGNED_CHAR:1,
|
||||
VTK_UNSIGNED_CHAR:1,
|
||||
VTK_SHORT:2,
|
||||
VTK_UNSIGNED_SHORT:2,
|
||||
VTK_INT:4,
|
||||
VTK_UNSIGNED_INT:4,
|
||||
VTK_FLOAT:4,
|
||||
VTK_DOUBLE:8 }
|
||||
|
||||
# convert unsigned shorts to ints, to avoid sign problems
|
||||
def SetConvertUnsignedShortToInt(self,yesno):
|
||||
self.__ConvertUnsignedShortToInt = yesno
|
||||
|
||||
def GetConvertUnsignedShortToInt(self):
|
||||
return self.__ConvertUnsignedShortToInt
|
||||
|
||||
def ConvertUnsignedShortToIntOn(self):
|
||||
self.__ConvertUnsignedShortToInt = True
|
||||
|
||||
def ConvertUnsignedShortToIntOff(self):
|
||||
self.__ConvertUnsignedShortToInt = False
|
||||
|
||||
# set the input
|
||||
def SetInputConnection(self,input):
|
||||
return self.__export.SetInputConnection(input)
|
||||
|
||||
def SetInputData(self,input):
|
||||
return self.__export.SetInputData(input)
|
||||
|
||||
def GetInput(self):
|
||||
return self.__export.GetInput()
|
||||
|
||||
def GetArray(self):
|
||||
self.__export.Update()
|
||||
input = self.__export.GetInput()
|
||||
extent = input.GetExtent()
|
||||
type = input.GetScalarType()
|
||||
numComponents = input.GetNumberOfScalarComponents()
|
||||
dim = (extent[5]-extent[4]+1,
|
||||
extent[3]-extent[2]+1,
|
||||
extent[1]-extent[0]+1)
|
||||
if (numComponents > 1):
|
||||
dim = dim + (numComponents,)
|
||||
|
||||
imArray = numpy.zeros(dim, self.__typeDict[type])
|
||||
self.__export.Export(imArray)
|
||||
|
||||
# convert unsigned short to int to avoid sign issues
|
||||
if (type == VTK_UNSIGNED_SHORT and self.__ConvertUnsignedShortToInt):
|
||||
imArray = umath.bitwise_and(imArray.astype('i'),0xffff)
|
||||
|
||||
return imArray
|
||||
|
||||
def GetDataExtent(self):
|
||||
return self.__export.GetDataExtent()
|
||||
|
||||
def GetDataSpacing(self):
|
||||
return self.__export.GetDataSpacing()
|
||||
|
||||
def GetDataOrigin(self):
|
||||
return self.__export.GetDataOrigin()
|
||||
@@ -0,0 +1,148 @@
|
||||
"""
|
||||
vtkImageImportFromArray: a NumPy front-end to vtkImageImport
|
||||
|
||||
Load a python array into a vtk image.
|
||||
To use this class, you must have NumPy installed (http://numpy.scipy.org/)
|
||||
|
||||
Methods:
|
||||
|
||||
SetArray() -- set the numpy array to load
|
||||
Update() -- generate the output
|
||||
GetOutput() -- get the image as vtkImageData
|
||||
GetOutputPort() -- connect to VTK pipeline
|
||||
|
||||
Methods from vtkImageImport:
|
||||
(if you don't set these, sensible defaults will be used)
|
||||
|
||||
SetDataExtent()
|
||||
SetDataSpacing()
|
||||
SetDataOrigin()
|
||||
"""
|
||||
|
||||
from vtkmodules.vtkIOImage import vtkImageImport
|
||||
from vtkmodules.vtkCommonCore import VTK_SIGNED_CHAR
|
||||
from vtkmodules.vtkCommonCore import VTK_UNSIGNED_CHAR
|
||||
from vtkmodules.vtkCommonCore import VTK_SHORT
|
||||
from vtkmodules.vtkCommonCore import VTK_UNSIGNED_SHORT
|
||||
from vtkmodules.vtkCommonCore import VTK_INT
|
||||
from vtkmodules.vtkCommonCore import VTK_UNSIGNED_INT
|
||||
from vtkmodules.vtkCommonCore import VTK_LONG
|
||||
from vtkmodules.vtkCommonCore import VTK_UNSIGNED_LONG
|
||||
from vtkmodules.vtkCommonCore import VTK_FLOAT
|
||||
from vtkmodules.vtkCommonCore import VTK_DOUBLE
|
||||
|
||||
class vtkImageImportFromArray:
|
||||
def __init__(self):
|
||||
self.__import = vtkImageImport()
|
||||
self.__ConvertIntToUnsignedShort = False
|
||||
self.__Array = None
|
||||
|
||||
# type dictionary: note that python doesn't support
|
||||
# unsigned integers properly!
|
||||
__typeDict = {'b':VTK_SIGNED_CHAR, # int8
|
||||
'B':VTK_UNSIGNED_CHAR, # uint8
|
||||
'h':VTK_SHORT, # int16
|
||||
'H':VTK_UNSIGNED_SHORT, # uint16
|
||||
'i':VTK_INT, # int32
|
||||
'I':VTK_UNSIGNED_INT, # uint32
|
||||
'f':VTK_FLOAT, # float32
|
||||
'd':VTK_DOUBLE, # float64
|
||||
'F':VTK_FLOAT, # float32
|
||||
'D':VTK_DOUBLE, # float64
|
||||
}
|
||||
|
||||
__sizeDict = { VTK_SIGNED_CHAR:1,
|
||||
VTK_UNSIGNED_CHAR:1,
|
||||
VTK_SHORT:2,
|
||||
VTK_UNSIGNED_SHORT:2,
|
||||
VTK_INT:4,
|
||||
VTK_UNSIGNED_INT:4,
|
||||
VTK_FLOAT:4,
|
||||
VTK_DOUBLE:8 }
|
||||
|
||||
# convert 'Int32' to 'unsigned short'
|
||||
def SetConvertIntToUnsignedShort(self,yesno):
|
||||
self.__ConvertIntToUnsignedShort = yesno
|
||||
|
||||
def GetConvertIntToUnsignedShort(self):
|
||||
return self.__ConvertIntToUnsignedShort
|
||||
|
||||
def ConvertIntToUnsignedShortOn(self):
|
||||
self.__ConvertIntToUnsignedShort = True
|
||||
|
||||
def ConvertIntToUnsignedShortOff(self):
|
||||
self.__ConvertIntToUnsignedShort = False
|
||||
|
||||
def Update(self):
|
||||
self.__import.Update()
|
||||
|
||||
# get the output
|
||||
def GetOutputPort(self):
|
||||
return self.__import.GetOutputPort()
|
||||
|
||||
# get the output
|
||||
def GetOutput(self):
|
||||
return self.__import.GetOutput()
|
||||
|
||||
# import an array
|
||||
def SetArray(self,imArray):
|
||||
self.__Array = imArray
|
||||
numComponents = 1
|
||||
dim = imArray.shape
|
||||
if len(dim) == 0:
|
||||
dim = (1,1,1)
|
||||
elif len(dim) == 1:
|
||||
dim = (1, 1, dim[0])
|
||||
elif len(dim) == 2:
|
||||
dim = (1, dim[0], dim[1])
|
||||
elif len(dim) == 4:
|
||||
numComponents = dim[3]
|
||||
dim = (dim[0],dim[1],dim[2])
|
||||
|
||||
typecode = imArray.dtype.char
|
||||
|
||||
ar_type = self.__typeDict[typecode]
|
||||
|
||||
complexComponents = 1
|
||||
if (typecode == 'F' or typecode == 'D'):
|
||||
numComponents = numComponents * 2
|
||||
complexComponents = 2
|
||||
|
||||
if (self.__ConvertIntToUnsignedShort and typecode == 'i'):
|
||||
imArray = imArray.astype('h')
|
||||
ar_type = VTK_UNSIGNED_SHORT
|
||||
|
||||
size = len(imArray.flat)*self.__sizeDict[ar_type]*complexComponents
|
||||
self.__import.CopyImportVoidPointer(imArray, size)
|
||||
self.__import.SetDataScalarType(ar_type)
|
||||
self.__import.SetNumberOfScalarComponents(numComponents)
|
||||
extent = self.__import.GetDataExtent()
|
||||
self.__import.SetDataExtent(extent[0],extent[0]+dim[2]-1,
|
||||
extent[2],extent[2]+dim[1]-1,
|
||||
extent[4],extent[4]+dim[0]-1)
|
||||
self.__import.SetWholeExtent(extent[0],extent[0]+dim[2]-1,
|
||||
extent[2],extent[2]+dim[1]-1,
|
||||
extent[4],extent[4]+dim[0]-1)
|
||||
|
||||
def GetArray(self):
|
||||
return self.__Array
|
||||
|
||||
# a whole bunch of methods copied from vtkImageImport
|
||||
|
||||
def SetDataExtent(self,extent):
|
||||
self.__import.SetDataExtent(extent)
|
||||
|
||||
def GetDataExtent(self):
|
||||
return self.__import.GetDataExtent()
|
||||
|
||||
def SetDataSpacing(self,spacing):
|
||||
self.__import.SetDataSpacing(spacing)
|
||||
|
||||
def GetDataSpacing(self):
|
||||
return self.__import.GetDataSpacing()
|
||||
|
||||
def SetDataOrigin(self,origin):
|
||||
self.__import.SetDataOrigin(origin)
|
||||
|
||||
def GetDataOrigin(self):
|
||||
return self.__import.GetDataOrigin()
|
||||
@@ -0,0 +1,226 @@
|
||||
"""
|
||||
This python module provides functionality to parse the methods of a
|
||||
VTK object.
|
||||
|
||||
Created by Prabhu Ramachandran. Committed in Apr, 2002.
|
||||
|
||||
"""
|
||||
|
||||
import string, re, sys
|
||||
import types
|
||||
|
||||
# set this to 1 if you want to see debugging messages - very useful if
|
||||
# you have problems
|
||||
DEBUG=0
|
||||
|
||||
def debug(msg):
|
||||
if DEBUG:
|
||||
print(msg)
|
||||
|
||||
class VtkDirMethodParser:
|
||||
"""Parses the methods from dir(vtk_obj)."""
|
||||
|
||||
def initialize_methods(self, vtk_obj):
|
||||
debug("VtkDirMethodParser:: initialize_methods()")
|
||||
|
||||
self.methods = dir(vtk_obj)[:]
|
||||
# stores the <blah>On methods
|
||||
self.toggle_meths = []
|
||||
# stores the Set<blah>To<blah> methods
|
||||
self.state_meths = []
|
||||
# stores the methods that have a Get<blah> and Set<blah>
|
||||
# only the <blah> is stored
|
||||
self.get_set_meths = []
|
||||
# pure get methods
|
||||
self.get_meths = []
|
||||
self.state_patn = re.compile("To[A-Z0-9]")
|
||||
|
||||
def parse_methods(self, vtk_obj):
|
||||
debug("VtkDirMethodParser:: parse_methods()")
|
||||
self.initialize_methods(vtk_obj)
|
||||
debug("VtkDirMethodParser:: parse_methods() - initialized methods")
|
||||
|
||||
for method in self.methods[:]:
|
||||
# finding all the methods that set the state.
|
||||
if method[:3].find("Set") >= 0 and \
|
||||
self.state_patn.search(method) is not None:
|
||||
try:
|
||||
eval("vtk_obj.Get%s" % method[3:])
|
||||
except AttributeError:
|
||||
self.state_meths.append(method)
|
||||
self.methods.remove(method)
|
||||
# finding all the On/Off toggle methods
|
||||
elif method[-2:].find("On") >= 0:
|
||||
try:
|
||||
self.methods.index("%sOff" % method[:-2])
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
self.toggle_meths.append(method)
|
||||
self.methods.remove(method)
|
||||
self.methods.remove("%sOff" % method[:-2])
|
||||
# finding the Get/Set methods.
|
||||
elif method[:3].find("Get") == 0:
|
||||
set_m = "Set" + method[3:]
|
||||
try:
|
||||
self.methods.index(set_m)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
self.get_set_meths.append(method[3:])
|
||||
self.methods.remove(method)
|
||||
self.methods.remove(set_m)
|
||||
|
||||
self.clean_up_methods(vtk_obj)
|
||||
|
||||
def clean_up_methods(self, vtk_obj):
|
||||
self.clean_get_set(vtk_obj)
|
||||
self.clean_state_methods(vtk_obj)
|
||||
self.clean_get_methods(vtk_obj)
|
||||
|
||||
def clean_get_set(self, vtk_obj):
|
||||
debug("VtkDirMethodParser:: clean_get_set()")
|
||||
# cleaning up the Get/Set methods by removing the toggle funcs.
|
||||
for method in self.toggle_meths:
|
||||
try:
|
||||
self.get_set_meths.remove(method[:-2])
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# cleaning them up by removing any methods that are responsible for
|
||||
# other vtkObjects
|
||||
for method in self.get_set_meths[:]:
|
||||
try:
|
||||
eval("vtk_obj.Get%s().GetClassName()" % method)
|
||||
except (TypeError, AttributeError):
|
||||
pass
|
||||
else:
|
||||
self.get_set_meths.remove(method)
|
||||
continue
|
||||
try:
|
||||
val = eval("vtk_obj.Get%s()" % method)
|
||||
except (TypeError, AttributeError):
|
||||
self.get_set_meths.remove(method)
|
||||
else:
|
||||
if val is None:
|
||||
self.get_set_meths.remove(method)
|
||||
|
||||
def clean_state_methods(self, vtk_obj):
|
||||
debug("VtkDirMethodParser:: clean_state_methods()")
|
||||
# Getting the remaining pure GetMethods
|
||||
for method in self.methods[:]:
|
||||
if method[:3].find("Get") == 0:
|
||||
self.get_meths.append(method)
|
||||
self.methods.remove(method)
|
||||
|
||||
# Grouping similar state methods
|
||||
if len(self.state_meths) != 0:
|
||||
tmp = self.state_meths[:]
|
||||
self.state_meths = []
|
||||
state_group = [tmp[0]]
|
||||
end = self.state_patn.search(tmp[0]).start()
|
||||
# stores the method type common to all similar methods
|
||||
m = tmp[0][3:end]
|
||||
for i in range(1, len(tmp)):
|
||||
if tmp[i].find(m) >= 0:
|
||||
state_group.append(tmp[i])
|
||||
else:
|
||||
self.state_meths.append(state_group)
|
||||
state_group = [tmp[i]]
|
||||
end = self.state_patn.search(tmp[i]).start()
|
||||
m = tmp[i][3:end]
|
||||
try: # remove the corresponding set method in get_set
|
||||
val = self.get_set_meths.index(m)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
del self.get_set_meths[val]
|
||||
#self.get_meths.append("Get" + m)
|
||||
clamp_m = "Get" + m + "MinValue"
|
||||
try: # remove the GetNameMax/MinValue in get_meths
|
||||
val = self.get_meths.index(clamp_m)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
del self.get_meths[val]
|
||||
val = self.get_meths.index("Get" + m + "MaxValue")
|
||||
del self.get_meths[val]
|
||||
|
||||
if len(state_group) > 0:
|
||||
self.state_meths.append(state_group)
|
||||
|
||||
def clean_get_methods(self, vtk_obj):
|
||||
debug("VtkDirMethodParser:: clean_get_methods()")
|
||||
for method in self.get_meths[:]:
|
||||
debug(method)
|
||||
try:
|
||||
res = eval("vtk_obj.%s()" % method)
|
||||
except (TypeError, AttributeError):
|
||||
self.get_meths.remove(method)
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
eval("vtk_obj.%s().GetClassName()" % method)
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
self.get_meths.remove(method)
|
||||
continue
|
||||
if method[-8:].find("MaxValue") > -1:
|
||||
self.get_meths.remove(method)
|
||||
elif method[-8:].find("MinValue") > -1:
|
||||
self.get_meths.remove(method)
|
||||
|
||||
self.get_meths.sort()
|
||||
|
||||
def toggle_methods(self):
|
||||
return self.toggle_meths
|
||||
|
||||
def state_methods(self):
|
||||
return self.state_meths
|
||||
|
||||
def get_set_methods(self):
|
||||
return self.get_set_meths
|
||||
|
||||
def get_methods(self):
|
||||
return self.get_meths
|
||||
|
||||
|
||||
class VtkPrintMethodParser:
|
||||
"""This class finds the methods for a given vtkObject. It uses
|
||||
the output from vtkObject->Print() (or in Python str(vtkObject))
|
||||
and output from the VtkDirMethodParser to obtain the methods."""
|
||||
|
||||
def parse_methods(self, vtk_obj):
|
||||
"""Parse for the methods."""
|
||||
debug("VtkPrintMethodParser:: parse_methods()")
|
||||
self._initialize_methods(vtk_obj)
|
||||
|
||||
def _get_str_obj(self, vtk_obj):
|
||||
debug("VtkPrintMethodParser:: _get_str_obj()")
|
||||
self.methods = str(vtk_obj)
|
||||
self.methods = self.methods.split("\n")
|
||||
del self.methods[0]
|
||||
|
||||
def _initialize_methods(self, vtk_obj):
|
||||
"""Do the basic parsing and setting up"""
|
||||
debug("VtkPrintMethodParser:: _initialize_methods()")
|
||||
dir_p = VtkDirMethodParser()
|
||||
dir_p.parse_methods(vtk_obj)
|
||||
|
||||
self.toggle_meths = dir_p.toggle_methods()
|
||||
self.state_meths = dir_p.state_methods()
|
||||
self.get_set_meths = dir_p.get_set_methods()
|
||||
self.get_meths = dir_p.get_methods()
|
||||
|
||||
def toggle_methods(self):
|
||||
return self.toggle_meths
|
||||
|
||||
def state_methods(self):
|
||||
return self.state_meths
|
||||
|
||||
def get_set_methods(self):
|
||||
return self.get_set_meths
|
||||
|
||||
def get_methods(self):
|
||||
return self.get_meths
|
||||
@@ -0,0 +1,205 @@
|
||||
"""
|
||||
Utility functions to mimic the template support functions for vtkVariant
|
||||
"""
|
||||
|
||||
from vtkmodules import vtkCommonCore
|
||||
import sys
|
||||
|
||||
_variant_type_map = {
|
||||
'void' : vtkCommonCore.VTK_VOID,
|
||||
'char' : vtkCommonCore.VTK_CHAR,
|
||||
'unsigned char' : vtkCommonCore.VTK_UNSIGNED_CHAR,
|
||||
'signed char' : vtkCommonCore.VTK_SIGNED_CHAR,
|
||||
'short' : vtkCommonCore.VTK_SHORT,
|
||||
'unsigned short' : vtkCommonCore.VTK_UNSIGNED_SHORT,
|
||||
'int' : vtkCommonCore.VTK_INT,
|
||||
'unsigned int' : vtkCommonCore.VTK_UNSIGNED_INT,
|
||||
'long' : vtkCommonCore.VTK_LONG,
|
||||
'unsigned long' : vtkCommonCore.VTK_UNSIGNED_LONG,
|
||||
'long long' : vtkCommonCore.VTK_LONG_LONG,
|
||||
'unsigned long long' : vtkCommonCore.VTK_UNSIGNED_LONG_LONG,
|
||||
'float' : vtkCommonCore.VTK_FLOAT,
|
||||
'double' : vtkCommonCore.VTK_DOUBLE,
|
||||
'string' : vtkCommonCore.VTK_STRING,
|
||||
'vtkObjectBase' : vtkCommonCore.VTK_OBJECT,
|
||||
'vtkObject' : vtkCommonCore.VTK_OBJECT,
|
||||
}
|
||||
|
||||
_variant_method_map = {
|
||||
vtkCommonCore.VTK_VOID : '',
|
||||
vtkCommonCore.VTK_CHAR : 'ToChar',
|
||||
vtkCommonCore.VTK_UNSIGNED_CHAR : 'ToUnsignedChar',
|
||||
vtkCommonCore.VTK_SIGNED_CHAR : 'ToSignedChar',
|
||||
vtkCommonCore.VTK_SHORT : 'ToShort',
|
||||
vtkCommonCore.VTK_UNSIGNED_SHORT : 'ToUnsignedShort',
|
||||
vtkCommonCore.VTK_INT : 'ToInt',
|
||||
vtkCommonCore.VTK_UNSIGNED_INT : 'ToUnsignedInt',
|
||||
vtkCommonCore.VTK_LONG : 'ToLong',
|
||||
vtkCommonCore.VTK_UNSIGNED_LONG : 'ToUnsignedLong',
|
||||
vtkCommonCore.VTK_LONG_LONG : 'ToLongLong',
|
||||
vtkCommonCore.VTK_UNSIGNED_LONG_LONG : 'ToUnsignedLongLong',
|
||||
vtkCommonCore.VTK_FLOAT : 'ToFloat',
|
||||
vtkCommonCore.VTK_DOUBLE : 'ToDouble',
|
||||
vtkCommonCore.VTK_STRING : 'ToString',
|
||||
vtkCommonCore.VTK_OBJECT : 'ToVTKObject',
|
||||
}
|
||||
|
||||
_variant_check_map = {
|
||||
vtkCommonCore.VTK_VOID : 'IsValid',
|
||||
vtkCommonCore.VTK_CHAR : 'IsChar',
|
||||
vtkCommonCore.VTK_UNSIGNED_CHAR : 'IsUnsignedChar',
|
||||
vtkCommonCore.VTK_SIGNED_CHAR : 'IsSignedChar',
|
||||
vtkCommonCore.VTK_SHORT : 'IsShort',
|
||||
vtkCommonCore.VTK_UNSIGNED_SHORT : 'IsUnsignedShort',
|
||||
vtkCommonCore.VTK_INT : 'IsInt',
|
||||
vtkCommonCore.VTK_UNSIGNED_INT : 'IsUnsignedInt',
|
||||
vtkCommonCore.VTK_LONG : 'IsLong',
|
||||
vtkCommonCore.VTK_UNSIGNED_LONG : 'IsUnsignedLong',
|
||||
vtkCommonCore.VTK_LONG_LONG : 'IsLongLong',
|
||||
vtkCommonCore.VTK_UNSIGNED_LONG_LONG : 'IsUnsignedLongLong',
|
||||
vtkCommonCore.VTK_FLOAT : 'IsFloat',
|
||||
vtkCommonCore.VTK_DOUBLE : 'IsDouble',
|
||||
vtkCommonCore.VTK_STRING : 'IsString',
|
||||
vtkCommonCore.VTK_OBJECT : 'IsVTKObject',
|
||||
}
|
||||
|
||||
|
||||
def vtkVariantCreate(v, t):
|
||||
"""
|
||||
Create a vtkVariant of the specified type, where the type is in the
|
||||
following format: 'int', 'unsigned int', etc. for numeric types,
|
||||
and 'string' for strings. You can also use an
|
||||
integer VTK type constant for the type.
|
||||
"""
|
||||
if not issubclass(type(t), int):
|
||||
t = _variant_type_map[t]
|
||||
|
||||
return vtkCommonCore.vtkVariant(v, t)
|
||||
|
||||
|
||||
def vtkVariantExtract(v, t=None):
|
||||
"""
|
||||
Extract the specified value type from the vtkVariant, where the type is
|
||||
in the following format: 'int', 'unsigned int', etc. for numeric types,
|
||||
and 'string' for strings. You can also use an
|
||||
integer VTK type constant for the type. Set the type to 'None" to
|
||||
extract the value in its native type.
|
||||
"""
|
||||
v = vtkCommonCore.vtkVariant(v)
|
||||
|
||||
if t == None:
|
||||
t = v.GetType()
|
||||
elif not issubclass(type(t), int):
|
||||
t = _variant_type_map[t]
|
||||
|
||||
if getattr(v, _variant_check_map[t])():
|
||||
return getattr(v, _variant_method_map[t])()
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def vtkVariantCast(v, t):
|
||||
"""
|
||||
Cast the vtkVariant to the specified value type, where the type is
|
||||
in the following format: 'int', 'unsigned int', etc. for numeric types,
|
||||
and 'string' for strings. You can also use an
|
||||
integer VTK type constant for the type.
|
||||
"""
|
||||
if not issubclass(type(t), int):
|
||||
t = _variant_type_map[t]
|
||||
|
||||
v = vtkCommonCore.vtkVariant(v, t)
|
||||
|
||||
if v.IsValid():
|
||||
return getattr(v, _variant_method_map[t])()
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def vtkVariantStrictWeakOrder(s1, s2):
|
||||
"""
|
||||
Compare variants by type first, and then by value.
|
||||
"""
|
||||
s1 = vtkCommonCore.vtkVariant(s1)
|
||||
s2 = vtkCommonCore.vtkVariant(s2)
|
||||
|
||||
t1 = s1.GetType()
|
||||
t2 = s2.GetType()
|
||||
|
||||
# check based on type
|
||||
if t1 != t2:
|
||||
return t1 < t2
|
||||
|
||||
v1 = s1.IsValid()
|
||||
v2 = s2.IsValid()
|
||||
|
||||
# check based on validity
|
||||
if (not v1) or (not v2):
|
||||
return v1 < v2
|
||||
|
||||
# extract and compare the values
|
||||
r1 = getattr(s1, _variant_method_map[t1])()
|
||||
r2 = getattr(s2, _variant_method_map[t2])()
|
||||
|
||||
# compare vtk objects by classname, then address
|
||||
if t1 == vtkCommonCore.VTK_OBJECT:
|
||||
c1 = r1.GetClassName()
|
||||
c2 = r2.GetClassName()
|
||||
if c1 != c2:
|
||||
return c1 < c2
|
||||
else:
|
||||
return r1.__this__ < r2.__this__
|
||||
|
||||
return r1 < r2
|
||||
|
||||
|
||||
class vtkVariantStrictWeakOrderKey:
|
||||
"""A key method (class, actually) for use with sort()"""
|
||||
def __init__(self, obj, *args):
|
||||
self.obj = obj
|
||||
def __lt__(self, other):
|
||||
return vtkVariantStrictWeakOrder(self.obj, other)
|
||||
|
||||
|
||||
def vtkVariantStrictEquality(s1, s2):
|
||||
"""
|
||||
Check two variants for strict equality of type and value.
|
||||
"""
|
||||
s1 = vtkCommonCore.vtkVariant(s1)
|
||||
s2 = vtkCommonCore.vtkVariant(s2)
|
||||
|
||||
t1 = s1.GetType()
|
||||
t2 = s2.GetType()
|
||||
|
||||
# check based on type
|
||||
if t1 != t2:
|
||||
return False
|
||||
|
||||
v1 = s1.IsValid()
|
||||
v2 = s2.IsValid()
|
||||
|
||||
# check based on validity
|
||||
if (not v1) and (not v2):
|
||||
return True
|
||||
elif v1 != v2:
|
||||
return False
|
||||
|
||||
# extract and compare the values
|
||||
r1 = getattr(s1, _variant_method_map[t1])()
|
||||
r2 = getattr(s2, _variant_method_map[t2])()
|
||||
|
||||
return (r1 == r2)
|
||||
|
||||
|
||||
def vtkVariantLessThan(s1, s2):
|
||||
"""
|
||||
Return true if s1 < s2.
|
||||
"""
|
||||
return (vtkCommonCore.vtkVariant(s1) < vtkCommonCore.vtkVariant(s2))
|
||||
|
||||
|
||||
def vtkVariantEqual(s1, s2):
|
||||
"""
|
||||
Return true if s1 == s2.
|
||||
"""
|
||||
return (vtkCommonCore.vtkVariant(s1) == vtkCommonCore.vtkVariant(s2))
|
||||
@@ -0,0 +1,397 @@
|
||||
import cftime
|
||||
import logging
|
||||
import numpy as np
|
||||
from os.path import basename, splitext, exists
|
||||
import xarray as xr
|
||||
from vtkmodules.vtkCommonCore import (
|
||||
vtkVariant,
|
||||
)
|
||||
from vtkmodules.vtkCommonDataModel import (
|
||||
vtkDataObject
|
||||
)
|
||||
from vtkmodules.vtkCommonExecutionModel import (
|
||||
vtkAlgorithm,
|
||||
vtkStreamingDemandDrivenPipeline
|
||||
)
|
||||
from vtkmodules.vtkIONetCDF import vtkNetCDFCFReader, vtkXArrayAccessor
|
||||
from vtkmodules.util import numpy_support
|
||||
from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase
|
||||
|
||||
@xr.register_dataset_accessor("vtk")
|
||||
class VtkAccessor:
|
||||
def __init__(self, dsxr):
|
||||
self._dsxr = dsxr
|
||||
|
||||
def create_reader(self):
|
||||
'''
|
||||
Returns a vtkXArrayCFReader that reads data from the XArray
|
||||
(using zero-copy when possible). At the moment, data is copied
|
||||
for coordinates (because they are converted to double in the reader)
|
||||
and for certain data that is subset either in XArray or in VTK.
|
||||
Lazy loading in XArray is respected, that is data is accessed only when
|
||||
it is needed.
|
||||
Time is passed to VTK either as an int64 for datetime64 or timedelta64,
|
||||
or as a double (using cftime.toordinal) for cftime.
|
||||
'''
|
||||
reader = vtkXArrayCFReader()
|
||||
reader.SetXArray(self._dsxr)
|
||||
return reader
|
||||
|
||||
|
||||
class vtkXArrayCFReader(VTKPythonAlgorithmBase):
|
||||
'''Reads data from a file using the XArray readers and then connects
|
||||
the XArray data to the vtkNetCDFCFREader (using zero-copy when
|
||||
possible). At the moment, data is copied for coordinates (because
|
||||
they are converted to double in the reader) and for certain data
|
||||
that is subset either in XArray or in VTK. Lazy loading in XArray
|
||||
is respected, that is data is accessed only when it is needed.
|
||||
Time is passed to VTK either as an int64 for datetime64 or
|
||||
timedelta64, or as a double (using cftime.toordinal) for cftime.
|
||||
'''
|
||||
|
||||
_FORWARD_GET = {
|
||||
"GetAccessor",
|
||||
"GetAllDimensions",
|
||||
|
||||
"GetNumberOfVariableArrays",
|
||||
"GetAllVariableArrayNames",
|
||||
"GetVariableArrayName",
|
||||
"GetVariableArrayStatus",
|
||||
|
||||
"GetTimeDimensionName",
|
||||
"GetLatitudeDimensionName",
|
||||
"GetLongitudeDimensionName",
|
||||
"GetVerticalDimensionName",
|
||||
|
||||
"GetOutput",
|
||||
"GetOutputType",
|
||||
"GetSphericalCoordinates",
|
||||
|
||||
"GetReplaceFillValueWithNan",
|
||||
|
||||
"GetVariableDimensions",
|
||||
"GetVerticalBias",
|
||||
"GetVerticalScale",
|
||||
"PrintSelf",
|
||||
}
|
||||
_FORWARD_SET = {
|
||||
"SetDimensions",
|
||||
|
||||
"SetTimeDimensionName",
|
||||
"SetLatitudeDimensionName",
|
||||
"SetLongitudeDimensionName",
|
||||
"SetVerticalDimensionName",
|
||||
|
||||
|
||||
"SetSphericalCoordinates",
|
||||
"SphericalCoordinatesOn",
|
||||
"SphericalCoordinatesOff",
|
||||
|
||||
"SetReplaceFillValueWithNan",
|
||||
"ReplaceFillValueWithNanOn",
|
||||
"ReplaceFillValueWithNanOff",
|
||||
|
||||
"SetOutputType",
|
||||
"SetOutputTypeToAutomatic",
|
||||
"SetOutputTypeToImage",
|
||||
"SetOutputTypeToRectilinear",
|
||||
"SetOutputTypeToStructured",
|
||||
"SetOutputTypeToUnstructured",
|
||||
|
||||
"SetVariableArrayStatus",
|
||||
"SetVerticalBias",
|
||||
"SetVerticalScale",
|
||||
"UpdateMetaData",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
VTKPythonAlgorithmBase.__init__(
|
||||
self, nInputPorts=0, nOutputPorts=1, outputType="vtkDataObject"
|
||||
)
|
||||
self._log = logging.getLogger("vtkXArrayCFReader")
|
||||
self._filename = None
|
||||
self._timesteps = None
|
||||
self._timeindex = None
|
||||
self._node = None
|
||||
self._dsxr = None
|
||||
self._reader = vtkNetCDFCFReader()
|
||||
self._ndarray_cftime_toordinal = np.frompyfunc(vtkXArrayCFReader._cftime_toordinal, 1, 1)
|
||||
# reference to contiguous arrays so that they are not dealocated
|
||||
self._arrays = {}
|
||||
|
||||
|
||||
def __getattr__(self, name):
|
||||
in_set = name in self._FORWARD_SET
|
||||
in_get = name in self._FORWARD_GET
|
||||
if in_set or in_get:
|
||||
if in_set:
|
||||
self.Modified()
|
||||
return getattr(self._reader, name)
|
||||
else:
|
||||
raise AttributeError()
|
||||
|
||||
def SetFileName(self, name):
|
||||
"""Specify filename for the file to read."""
|
||||
if self._filename != name:
|
||||
self._filename = name
|
||||
self.Modified()
|
||||
|
||||
def GetFileName(self):
|
||||
return self._filename
|
||||
|
||||
def CanReadFile(self, filepath):
|
||||
ext = splitext(filepath)[1]
|
||||
filename = basename(filepath)
|
||||
correct_name = False
|
||||
if ext == '.nc' or ext == '.grib' or ext == '.h5':
|
||||
correct_name = True
|
||||
else:
|
||||
if ext == '' and filename == '.zgroup':
|
||||
correct_name = True
|
||||
if correct_name and exists(filepath):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def SetNode(self, node):
|
||||
if self._node != node:
|
||||
self._node = node
|
||||
self.Modified()
|
||||
|
||||
def GetNode(self):
|
||||
return self._node
|
||||
|
||||
def SetXArray(self, dsxr):
|
||||
self._dsxr = dsxr
|
||||
self._update_accessor()
|
||||
self.Modified()
|
||||
|
||||
def GetXArray(self):
|
||||
return self._dsxr
|
||||
|
||||
def RequestDataObject(self, request, inInfo, outInfo):
|
||||
self._log.debug(f"DataObject ======================================================================")
|
||||
if not self._dsxr:
|
||||
if self._node:
|
||||
tree = xr.open_datatree(self._filename)
|
||||
self._dsxr = tree[self._node].to_dataset()
|
||||
else:
|
||||
self._dsxr = xr.open_dataset(self._filename, decode_timedelta=True)
|
||||
self._update_accessor()
|
||||
self._reader.UpdateDataObject()
|
||||
roi = self._reader.GetOutputInformation(0)
|
||||
if roi.Has(vtkDataObject.DATA_OBJECT()):
|
||||
rdata = roi.Get(vtkDataObject.DATA_OBJECT())
|
||||
else:
|
||||
self._log.error("vtkNetCDFCFReader did not create the dataset")
|
||||
rdata = None
|
||||
oi = outInfo.GetInformationObject(0)
|
||||
oi.Set(vtkDataObject.DATA_OBJECT(), rdata)
|
||||
return 1
|
||||
|
||||
def RequestInformation(self, request, inInfo, outInfo):
|
||||
self._log.debug(f"Information ======================================================================")
|
||||
oi = outInfo.GetInformationObject(0)
|
||||
self._reader.UpdateInformation()
|
||||
roi = self._reader.GetOutputInformation(0)
|
||||
if roi.Has(vtkStreamingDemandDrivenPipeline.TIME_STEPS()):
|
||||
self._timesteps = roi.Get(vtkStreamingDemandDrivenPipeline.TIME_STEPS())
|
||||
oi.Set(vtkStreamingDemandDrivenPipeline.TIME_STEPS(), self._timesteps, len(self._timesteps))
|
||||
oi.Set(vtkStreamingDemandDrivenPipeline.TIME_RANGE(), [self._timesteps[0], self._timesteps[-1]], 2)
|
||||
self._timesteps = np.asarray(self._timesteps)
|
||||
if roi.Has(vtkStreamingDemandDrivenPipeline.WHOLE_EXTENT()):
|
||||
ext = roi.Get(vtkStreamingDemandDrivenPipeline.WHOLE_EXTENT())
|
||||
self._log.debug("Whole extent: {}".format(ext))
|
||||
oi.Set(vtkStreamingDemandDrivenPipeline.WHOLE_EXTENT(), ext, 6)
|
||||
if roi.Has(vtkAlgorithm.CAN_HANDLE_PIECE_REQUEST()):
|
||||
oi.Set(vtkAlgorithm.CAN_HANDLE_PIECE_REQUEST(), 1)
|
||||
if roi.Has(vtkAlgorithm.CAN_PRODUCE_SUB_EXTENT()):
|
||||
oi.Set(vtkAlgorithm.CAN_PRODUCE_SUB_EXTENT(), 1)
|
||||
return 1
|
||||
|
||||
def RequestUpdateExtent(self, request, inInfo, outInfo):
|
||||
self._log.debug(f"UpdateExtent ======================================================================")
|
||||
oi = outInfo.GetInformationObject(0)
|
||||
if oi.Has(vtkStreamingDemandDrivenPipeline.UPDATE_TIME_STEP()):
|
||||
utime = oi.Get(vtkStreamingDemandDrivenPipeline.UPDATE_TIME_STEP())
|
||||
timeindex = (np.abs(self._timesteps - utime)).argmin()
|
||||
if timeindex != self._timeindex:
|
||||
self._log.debug(f"Time index = {timeindex}")
|
||||
self._timeindex = timeindex
|
||||
self.Modified()
|
||||
if oi.Has(vtkStreamingDemandDrivenPipeline.UPDATE_EXTENT()):
|
||||
ext = [0, 0, 0, 0, 0, 0]
|
||||
oi.Get(vtkStreamingDemandDrivenPipeline.UPDATE_EXTENT(), ext)
|
||||
self._log.debug("Update extent: {}".format(ext))
|
||||
roi = self._reader.GetOutputInformation(0)
|
||||
roi.Set(vtkStreamingDemandDrivenPipeline.UPDATE_EXTENT(), ext, 6)
|
||||
self._reader.PropagateUpdateExtent()
|
||||
return 1
|
||||
|
||||
|
||||
def RequestData(self, request, inInfo, outInfo):
|
||||
self._log.debug(f"Data ======================================================================")
|
||||
if self._timeindex:
|
||||
dsxr = self._dsxr.isel({self.GetTimeDimensionName() : self._timeindex})
|
||||
else:
|
||||
# no time, so no aditional selection is needed
|
||||
dsxr = self._dsxr
|
||||
accessor = self._reader.GetAccessor()
|
||||
self._set_data_vars(accessor, dsxr)
|
||||
self._reader.Update()
|
||||
# self._reader's data is already set for this's data so no ShallowCopy is needed
|
||||
return 1
|
||||
|
||||
@staticmethod
|
||||
def _get_nc_type(numpy_array_type):
|
||||
"""Returns a nc_type given a numpy array."""
|
||||
NC_BYTE = 1 # 1 byte integer
|
||||
NC_CHAR = 2 # iso/ascii character
|
||||
NC_SHORT = 3 # 2 byte integer
|
||||
NC_INT = 4 # 4 byte integer
|
||||
NC_LONG = NC_INT
|
||||
NC_FLOAT = 5
|
||||
NC_DOUBLE = 6
|
||||
NC_UBYTE = 7
|
||||
NC_USHORT = 8
|
||||
NC_UINT = 9
|
||||
NC_INT64 = 10 # 8 bypte integer
|
||||
NC_UINT64 = 11
|
||||
NC_STRING = 12
|
||||
_np_nc = {
|
||||
np.uint8: NC_UBYTE,
|
||||
np.uint16: NC_USHORT,
|
||||
np.uint32: NC_UINT,
|
||||
np.uint64: NC_UINT64,
|
||||
np.int8: NC_BYTE,
|
||||
np.int16: NC_SHORT,
|
||||
np.int32: NC_INT,
|
||||
np.int64: NC_INT64,
|
||||
np.float32: NC_FLOAT,
|
||||
np.float64: NC_DOUBLE,
|
||||
np.datetime64: NC_INT64,
|
||||
np.timedelta64: NC_INT64,
|
||||
np.str_: NC_STRING,
|
||||
np.bytes_: NC_CHAR,
|
||||
}
|
||||
for key, nc_type in _np_nc.items():
|
||||
if (
|
||||
numpy_array_type == key
|
||||
or np.issubdtype(numpy_array_type, key)
|
||||
or numpy_array_type == np.dtype(key)
|
||||
):
|
||||
return nc_type
|
||||
raise TypeError(
|
||||
"Could not find a suitable NetCDF type for %s" % (str(numpy_array_type))
|
||||
)
|
||||
|
||||
def _update_accessor(self):
|
||||
accessor, timename = self._get_accessor()
|
||||
self._reader.SetAccessor(accessor)
|
||||
if timename:
|
||||
self._reader.SetTimeDimensionName(timename)
|
||||
|
||||
def _get_accessor(self):
|
||||
acclog = logging.getLogger("_get_accessor_")
|
||||
acclog.setLevel(logging.WARNING)
|
||||
accessor = vtkXArrayAccessor()
|
||||
time_name = None
|
||||
time_names = []
|
||||
# Set Dim and DimLen
|
||||
dimNameToIndex = {k: i for i, k in enumerate(self._dsxr.sizes.keys())}
|
||||
accessor.SetDim(list(self._dsxr.sizes.keys()))
|
||||
accessor.SetDimLen(list(self._dsxr.sizes.values()))
|
||||
|
||||
# Set Var
|
||||
varList = list(self._dsxr.data_vars.keys()) + list(self._dsxr.coords.keys())
|
||||
varNameToIndex = {k: i for i, k in enumerate(varList)}
|
||||
is_coord = [0] * len(self._dsxr.data_vars)
|
||||
is_coord = is_coord + [1] * len(self._dsxr.coords)
|
||||
coords_bounds = self._get_coords_bounds()
|
||||
accessor.SetVar(varList, is_coord)
|
||||
for i, v in enumerate(varList):
|
||||
# data_vars are set after array selection and time selection to
|
||||
# take advantage of xarray lazy loading
|
||||
# https://docs.xarray.dev/en/latest/internals/internal-design.html
|
||||
if is_coord[i] or v in coords_bounds:
|
||||
# if there is subsetting in xarray, self._dsxr[v].values is
|
||||
# not contiguous. If the array is not contiguous, a contiguous
|
||||
# copy is created otherwise the contiguous array is simply returned
|
||||
v_data = np.ascontiguousarray(self._dsxr[v].values)
|
||||
if (
|
||||
v_data.dtype.type == np.datetime64
|
||||
or v_data.dtype.type == np.timedelta64
|
||||
):
|
||||
un = np.datetime_data(v_data.dtype)
|
||||
# unit = ns and 1 base unit
|
||||
if un[0] == "ns" and un[1] == 1:
|
||||
time_names.append(v)
|
||||
if v_data.dtype.char == "O":
|
||||
# object array, assume cftime
|
||||
# copy cftime array to a doubles array
|
||||
self._arrays[v] = self._ndarray_cftime_toordinal(v_data).astype(np.float64)
|
||||
time_names.append(v)
|
||||
v_data = self._arrays[v]
|
||||
else:
|
||||
self._arrays[v] = v_data
|
||||
acclog.debug(f"{v=} {v_data.shape=} {v_data.dtype} {self._dsxr[v].dims=}")
|
||||
acclog.debug(f"address:{hex(v_data.ctypes.data)}")
|
||||
accessor.SetVarValue(i, v_data)
|
||||
accessor.SetVarType(i, vtkXArrayCFReader._get_nc_type(v_data.dtype))
|
||||
else:
|
||||
accessor.SetVarType(i, vtkXArrayCFReader._get_nc_type(self._dsxr[v].variable.dtype))
|
||||
accessor.SetVarDims(i, [dimNameToIndex[name] for name in self._dsxr[v].dims])
|
||||
accessor.SetVarCoords(
|
||||
i, [varNameToIndex[name] for name in self._dsxr[v].coords]
|
||||
)
|
||||
|
||||
acclog.debug("Attributes:")
|
||||
for item in self._dsxr[v].attrs.items():
|
||||
acclog.debug(
|
||||
"name: {} value: {} type: {}".format(
|
||||
item[0], item[1], type(item[1])
|
||||
)
|
||||
)
|
||||
if np.issubdtype(type(item[1]), np.integer):
|
||||
accessor.SetAtt(i, item[0], vtkVariant(int(item[1])))
|
||||
elif np.issubdtype(type(item[1]), np.floating):
|
||||
accessor.SetAtt(i, item[0], vtkVariant(float(item[1])))
|
||||
elif isinstance(item[1], np.ndarray):
|
||||
accessor.SetAtt(
|
||||
i, item[0], vtkVariant(numpy_support.numpy_to_vtk(item[1]))
|
||||
)
|
||||
else:
|
||||
accessor.SetAtt(i, item[0], vtkVariant(item[1]))
|
||||
if len(time_names) >= 1:
|
||||
for name in time_names:
|
||||
if accessor.IsCOARDSCoordinate(name):
|
||||
time_name = name
|
||||
break
|
||||
return accessor, time_name
|
||||
|
||||
def _set_data_vars(self, accessor, dsxr):
|
||||
# data_vars are listed first in the list of data_vars,coords so we don't
|
||||
# need to add coords to the list, and still get the corect indexes
|
||||
varList = list(dsxr.data_vars.keys())
|
||||
for i, v in enumerate(varList):
|
||||
if self._reader.GetVariableArrayStatus(v):
|
||||
v_data = np.ascontiguousarray(dsxr[v].values)
|
||||
accessor.SetVarValue(i, v_data)
|
||||
self._arrays[v] = v_data
|
||||
|
||||
def _get_coords_bounds(self):
|
||||
'''
|
||||
Special data_vars associated coords
|
||||
'''
|
||||
b=set()
|
||||
for coord in list(self._dsxr.coords):
|
||||
bounds_attr = 'bounds'
|
||||
if bounds_attr in self._dsxr[coord].attrs:
|
||||
b.add(self._dsxr[coord].attrs[bounds_attr])
|
||||
return b
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _cftime_toordinal(o):
|
||||
return o.toordinal(fractional=True)
|
||||
Reference in New Issue
Block a user