root / ase / visualize / vtk / data.py @ 3
Historique | Voir | Annoter | Télécharger (7,12 ko)
1 | 1 | tkerber | |
---|---|---|---|
2 | 1 | tkerber | import numpy as np |
3 | 1 | tkerber | from numpy.ctypeslib import ctypes |
4 | 1 | tkerber | |
5 | 1 | tkerber | from vtk import vtkDataArray, vtkFloatArray, vtkDoubleArray |
6 | 1 | tkerber | |
7 | 1 | tkerber | if ctypes is None: |
8 | 1 | tkerber | class CTypesEmulator: |
9 | 1 | tkerber | def __init__(self): |
10 | 1 | tkerber | self._SimpleCData = np.number
|
11 | 1 | tkerber | self.c_float = np.float32
|
12 | 1 | tkerber | self.c_double = np.float64
|
13 | 1 | tkerber | try:
|
14 | 1 | tkerber | import ctypes |
15 | 1 | tkerber | except ImportError: |
16 | 1 | tkerber | ctypes = CTypesEmulator() |
17 | 1 | tkerber | |
18 | 1 | tkerber | # -------------------------------------------------------------------
|
19 | 1 | tkerber | |
20 | 1 | tkerber | class vtkNumPyBuffer: |
21 | 1 | tkerber | def __init__(self, data): |
22 | 1 | tkerber | self.strbuf = data.tostring()
|
23 | 1 | tkerber | self.nitems = len(data.flat) |
24 | 1 | tkerber | |
25 | 1 | tkerber | def __len__(self): |
26 | 1 | tkerber | return self.nitems |
27 | 1 | tkerber | |
28 | 1 | tkerber | def get_pointer(self): |
29 | 1 | tkerber | # Any C/C++ method that requires a void * can be passed a Python
|
30 | 1 | tkerber | # string. No check is done to ensure that the string is the correct
|
31 | 1 | tkerber | # size, and the string's reference count is not incremented. Extreme
|
32 | 1 | tkerber | # caution should be applied when using this feature.
|
33 | 1 | tkerber | return self.strbuf |
34 | 1 | tkerber | |
35 | 1 | tkerber | def notify(self, obj, event): |
36 | 1 | tkerber | if event == 'DeleteEvent': |
37 | 1 | tkerber | del self.strbuf |
38 | 1 | tkerber | else:
|
39 | 1 | tkerber | raise RuntimeError('Event not recognized.') |
40 | 1 | tkerber | |
41 | 1 | tkerber | class vtkDataArrayFromNumPyBuffer: |
42 | 1 | tkerber | def __init__(self, vtk_class, ctype, data=None): |
43 | 1 | tkerber | |
44 | 1 | tkerber | assert issubclass(ctype, ctypes._SimpleCData) |
45 | 1 | tkerber | self.ctype = ctype
|
46 | 1 | tkerber | |
47 | 1 | tkerber | self.vtk_da = vtk_class()
|
48 | 1 | tkerber | assert isinstance(self.vtk_da, vtkDataArray) |
49 | 1 | tkerber | assert self.vtk_da.GetDataTypeSize() == np.nbytes[np.dtype(self.ctype)] |
50 | 1 | tkerber | |
51 | 1 | tkerber | if data is not None: |
52 | 1 | tkerber | self.read_numpy_array(data)
|
53 | 1 | tkerber | |
54 | 1 | tkerber | def read_numpy_array(self, data): |
55 | 1 | tkerber | |
56 | 1 | tkerber | if not isinstance(data, np.ndarray): |
57 | 1 | tkerber | data = np.array(data, dtype=self.ctype)
|
58 | 1 | tkerber | |
59 | 1 | tkerber | if data.dtype != self.ctype: # NB: "is not" gets it wrong |
60 | 1 | tkerber | data = data.astype(self.ctype)
|
61 | 1 | tkerber | |
62 | 1 | tkerber | self.vtk_da.SetNumberOfComponents(data.shape[-1]) |
63 | 1 | tkerber | |
64 | 1 | tkerber | # Passing the void* buffer to the C interface does not increase
|
65 | 1 | tkerber | # its reference count, hence the buffer is deleted by Python when
|
66 | 1 | tkerber | # the reference count of the string from tostring reaches zero.
|
67 | 1 | tkerber | # Also, the boolean True tells VTK to save (not delete) the buffer
|
68 | 1 | tkerber | # when the VTK data array is deleted - we want Python to do this.
|
69 | 1 | tkerber | npybuf = vtkNumPyBuffer(data) |
70 | 1 | tkerber | self.vtk_da.SetVoidArray(npybuf.get_pointer(), len(npybuf), True) |
71 | 1 | tkerber | self.vtk_da.AddObserver('DeleteEvent', npybuf.notify) |
72 | 1 | tkerber | |
73 | 1 | tkerber | def get_output(self): |
74 | 1 | tkerber | return self.vtk_da |
75 | 1 | tkerber | |
76 | 1 | tkerber | def copy(self): |
77 | 1 | tkerber | vtk_da_copy = self.vtk_da.NewInstance()
|
78 | 1 | tkerber | vtk_da_copy.SetNumberOfComponents(self.vtk_da.GetNumberOfComponents())
|
79 | 1 | tkerber | vtk_da_copy.SetNumberOfTuples(self.vtk_da.GetNumberOfTuples())
|
80 | 1 | tkerber | |
81 | 1 | tkerber | assert vtk_da_copy.GetSize() == self.vtk_da.GetSize() |
82 | 1 | tkerber | |
83 | 1 | tkerber | vtk_da_copy.DeepCopy(self.vtk_da)
|
84 | 1 | tkerber | |
85 | 1 | tkerber | return vtk_da_copy
|
86 | 1 | tkerber | |
87 | 1 | tkerber | # -------------------------------------------------------------------
|
88 | 1 | tkerber | |
89 | 1 | tkerber | class vtkDataArrayFromNumPyArray(vtkDataArrayFromNumPyBuffer): |
90 | 1 | tkerber | """Class for reading vtkDataArray from 1D or 2D NumPy array.
|
91 | 1 | tkerber |
|
92 | 1 | tkerber | This class can be used to generate a vtkDataArray from a NumPy array.
|
93 | 1 | tkerber | The NumPy array should be of the form <entries> x <number of components>
|
94 | 1 | tkerber | where 'number of components' indicates the number of components in
|
95 | 1 | tkerber | each entry in the vtkDataArray. Note that this form is also expected
|
96 | 1 | tkerber | even in the case of only a single component.
|
97 | 1 | tkerber | """
|
98 | 1 | tkerber | def __init__(self, vtk_class, ctype, data=None, buffered=True): |
99 | 1 | tkerber | |
100 | 1 | tkerber | self.buffered = buffered
|
101 | 1 | tkerber | |
102 | 1 | tkerber | vtkDataArrayFromNumPyBuffer.__init__(self, vtk_class, ctype, data)
|
103 | 1 | tkerber | |
104 | 1 | tkerber | def read_numpy_array(self, data): |
105 | 1 | tkerber | """Read vtkDataArray from NumPy array"""
|
106 | 1 | tkerber | |
107 | 1 | tkerber | if not isinstance(data, np.ndarray): |
108 | 1 | tkerber | data = np.array(data, dtype=self.ctype)
|
109 | 1 | tkerber | |
110 | 1 | tkerber | if data.dtype != self.ctype: # NB: "is not" gets it wrong |
111 | 1 | tkerber | data = data.astype(self.ctype)
|
112 | 1 | tkerber | |
113 | 1 | tkerber | if data.ndim == 1: |
114 | 1 | tkerber | data = data[:, np.newaxis] |
115 | 1 | tkerber | elif data.ndim != 2: |
116 | 1 | tkerber | raise ValueError('Data must be a 1D or 2D NumPy array.') |
117 | 1 | tkerber | |
118 | 1 | tkerber | if self.buffered: |
119 | 1 | tkerber | vtkDataArrayFromNumPyBuffer.read_numpy_array(self, data)
|
120 | 1 | tkerber | else:
|
121 | 1 | tkerber | self.vtk_da.SetNumberOfComponents(data.shape[-1]) |
122 | 1 | tkerber | self.vtk_da.SetNumberOfTuples(data.shape[0]) |
123 | 1 | tkerber | |
124 | 1 | tkerber | for i, d_c in enumerate(data): |
125 | 1 | tkerber | for c, d in enumerate(d_c): |
126 | 1 | tkerber | self.vtk_da.SetComponent(i, c, d)
|
127 | 1 | tkerber | |
128 | 1 | tkerber | class vtkFloatArrayFromNumPyArray(vtkDataArrayFromNumPyArray): |
129 | 1 | tkerber | def __init__(self, data): |
130 | 1 | tkerber | vtkDataArrayFromNumPyArray.__init__(self, vtkFloatArray,
|
131 | 1 | tkerber | ctypes.c_float, data) |
132 | 1 | tkerber | |
133 | 1 | tkerber | class vtkDoubleArrayFromNumPyArray(vtkDataArrayFromNumPyArray): |
134 | 1 | tkerber | def __init__(self, data): |
135 | 1 | tkerber | vtkDataArrayFromNumPyArray.__init__(self, vtkDoubleArray,
|
136 | 1 | tkerber | ctypes.c_double, data) |
137 | 1 | tkerber | |
138 | 1 | tkerber | # -------------------------------------------------------------------
|
139 | 1 | tkerber | |
140 | 1 | tkerber | class vtkDataArrayFromNumPyMultiArray(vtkDataArrayFromNumPyBuffer): |
141 | 1 | tkerber | """Class for reading vtkDataArray from a multi-dimensional NumPy array.
|
142 | 1 | tkerber |
|
143 | 1 | tkerber | This class can be used to generate a vtkDataArray from a NumPy array.
|
144 | 1 | tkerber | The NumPy array should be of the form <gridsize> x <number of components>
|
145 | 1 | tkerber | where 'number of components' indicates the number of components in
|
146 | 1 | tkerber | each gridpoint in the vtkDataArray. Note that this form is also expected
|
147 | 1 | tkerber | even in the case of only a single component.
|
148 | 1 | tkerber | """
|
149 | 1 | tkerber | def __init__(self, vtk_class, ctype, data=None, buffered=True): |
150 | 1 | tkerber | |
151 | 1 | tkerber | self.buffered = buffered
|
152 | 1 | tkerber | |
153 | 1 | tkerber | vtkDataArrayFromNumPyBuffer.__init__(self, vtk_class, ctype, data)
|
154 | 1 | tkerber | |
155 | 1 | tkerber | def read_numpy_array(self, data): |
156 | 1 | tkerber | """Read vtkDataArray from NumPy array"""
|
157 | 1 | tkerber | |
158 | 1 | tkerber | if not isinstance(data, np.ndarray): |
159 | 1 | tkerber | data = np.array(data, dtype=self.ctype)
|
160 | 1 | tkerber | |
161 | 1 | tkerber | if data.dtype != self.ctype: # NB: "is not" gets it wrong |
162 | 1 | tkerber | data = data.astype(self.ctype)
|
163 | 1 | tkerber | |
164 | 1 | tkerber | if data.ndim <=2: |
165 | 1 | tkerber | raise Warning('This is inefficient for 1D and 2D NumPy arrays. ' + |
166 | 1 | tkerber | 'Use a vtkDataArrayFromNumPyArray subclass instead.')
|
167 | 1 | tkerber | |
168 | 1 | tkerber | if self.buffered: |
169 | 1 | tkerber | # This is less than ideal, but will not copy data (uses views).
|
170 | 1 | tkerber | # To get the correct ordering, the grid dimensions have to be
|
171 | 1 | tkerber | # transposed without moving the last dimension (the components).
|
172 | 1 | tkerber | n = data.ndim-1
|
173 | 1 | tkerber | for c in range(n//2): |
174 | 1 | tkerber | data = data.swapaxes(c,n-1-c)
|
175 | 1 | tkerber | |
176 | 1 | tkerber | vtkDataArrayFromNumPyBuffer.read_numpy_array(self, data)
|
177 | 1 | tkerber | else:
|
178 | 1 | tkerber | self.vtk_da.SetNumberOfComponents(data.shape[-1]) |
179 | 1 | tkerber | self.vtk_da.SetNumberOfTuples(np.prod(data.shape[:-1])) |
180 | 1 | tkerber | |
181 | 1 | tkerber | for c, d_T in enumerate(data.T): |
182 | 1 | tkerber | for i, d in enumerate(d_T.flat): |
183 | 1 | tkerber | self.vtk_da.SetComponent(i, c, d)
|
184 | 1 | tkerber | |
185 | 1 | tkerber | class vtkFloatArrayFromNumPyMultiArray(vtkDataArrayFromNumPyMultiArray): |
186 | 1 | tkerber | def __init__(self, data): |
187 | 1 | tkerber | vtkDataArrayFromNumPyMultiArray.__init__(self, vtkFloatArray,
|
188 | 1 | tkerber | ctypes.c_float, data) |
189 | 1 | tkerber | |
190 | 1 | tkerber | class vtkDoubleArrayFromNumPyMultiArray(vtkDataArrayFromNumPyMultiArray): |
191 | 1 | tkerber | def __init__(self, data): |
192 | 1 | tkerber | vtkDataArrayFromNumPyMultiArray.__init__(self, vtkDoubleArray,
|
193 | 1 | tkerber | ctypes.c_double, data) |