This commit is contained in:
cjw
2026-02-12 23:22:11 +08:00
parent 7b09eb3d89
commit 89660bba4e
5988 changed files with 2517516 additions and 0 deletions
@@ -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)