|
1 | 1 | import dash_vtk
|
| 2 | +import base64 |
| 3 | + |
2 | 4 | try:
|
3 | 5 | # v9 and above
|
4 | 6 | from vtkmodules.util.numpy_support import vtk_to_numpy
|
|
11 | 13 |
|
12 | 14 | # Numpy to JS TypedArray
|
13 | 15 | to_js_type = {
|
14 |
| - 'int8': 'Int8Array', |
15 |
| - 'uint8': 'Uint8Array', |
16 |
| - 'int16': 'Int16Array', |
17 |
| - 'uint16': 'Uint16Array', |
18 |
| - 'int32': 'Int32Array', |
19 |
| - 'uint32': 'Uint32Array', |
20 |
| - 'int64': 'Int32Array', |
21 |
| - 'uint64': 'Uint32Array', |
22 |
| - 'float32': 'Float32Array', |
23 |
| - 'float64': 'Float64Array', |
| 16 | + "int8": "Int8Array", |
| 17 | + "uint8": "Uint8Array", |
| 18 | + "int16": "Int16Array", |
| 19 | + "uint16": "Uint16Array", |
| 20 | + "int32": "Int32Array", |
| 21 | + "uint32": "Uint32Array", |
| 22 | + "int64": "Int32Array", |
| 23 | + "uint64": "Uint32Array", |
| 24 | + "float32": "Float32Array", |
| 25 | + "float64": "Float64Array", |
24 | 26 | }
|
25 | 27 |
|
26 | 28 |
|
| 29 | +def b64_encode_numpy(obj): |
| 30 | + # Convert 1D numpy arrays with numeric types to memoryviews with |
| 31 | + # datatype and shape metadata. |
| 32 | + dtype = obj.dtype |
| 33 | + if ( |
| 34 | + dtype.kind in ["u", "i", "f"] |
| 35 | + and str(dtype) != "int64" |
| 36 | + and str(dtype) != "uint64" |
| 37 | + ): |
| 38 | + # We have a numpy array that is compatible with JavaScript typed |
| 39 | + # arrays |
| 40 | + buffer = base64.b64encode(memoryview(obj.ravel(order="C"))).decode("utf-8") |
| 41 | + return {"bvals": buffer, "dtype": str(dtype), "shape": obj.shape} |
| 42 | + else: |
| 43 | + # Convert all other numpy arrays to lists |
| 44 | + return obj.tolist() |
| 45 | + |
| 46 | + |
27 | 47 | def to_mesh_state(dataset, field_to_keep=None, point_arrays=None, cell_arrays=None):
|
28 |
| - '''Expect any dataset and extract its surface into a dash_vtk.Mesh state property''' |
29 |
| - if dataset is None: |
30 |
| - return None |
31 |
| - |
32 |
| - if point_arrays is None: |
33 |
| - point_arrays = [] |
34 |
| - if cell_arrays is None: |
35 |
| - cell_arrays = [] |
36 |
| - |
37 |
| - # Make sure we have a polydata to export |
38 |
| - polydata = None |
39 |
| - if dataset.IsA('vtkPolyData'): |
40 |
| - polydata = dataset |
41 |
| - else: |
42 |
| - extractSkinFilter = vtkGeometryFilter() |
43 |
| - extractSkinFilter.SetInputData(dataset) |
44 |
| - extractSkinFilter.Update() |
45 |
| - polydata = extractSkinFilter.GetOutput() |
46 |
| - |
47 |
| - if polydata.GetPoints() is None: |
48 |
| - return None |
49 |
| - |
50 |
| - # Extract mesh |
51 |
| - points = vtk_to_numpy(polydata.GetPoints().GetData()).ravel() |
52 |
| - verts = vtk_to_numpy(polydata.GetVerts().GetData() |
53 |
| - ) if polydata.GetVerts() else [] |
54 |
| - lines = vtk_to_numpy(polydata.GetLines().GetData() |
55 |
| - ) if polydata.GetLines() else [] |
56 |
| - polys = vtk_to_numpy(polydata.GetPolys().GetData() |
57 |
| - ) if polydata.GetPolys() else [] |
58 |
| - strips = vtk_to_numpy(polydata.GetStrips().GetData() |
59 |
| - ) if polydata.GetStrips() else [] |
60 |
| - |
61 |
| - # Extract field |
62 |
| - values = None |
63 |
| - js_types = 'Float32Array' |
64 |
| - nb_comp = 1 |
65 |
| - dataRange = [0, 1] |
66 |
| - location = None |
67 |
| - if field_to_keep is not None: |
68 |
| - p_array = polydata.GetPointData().GetArray(field_to_keep) |
69 |
| - c_array = polydata.GetCellData().GetArray(field_to_keep) |
70 |
| - |
71 |
| - if c_array: |
72 |
| - dataRange = c_array.GetRange(-1) |
73 |
| - nb_comp = c_array.GetNumberOfComponents() |
74 |
| - values = vtk_to_numpy(c_array).ravel() |
75 |
| - js_types = to_js_type[str(values.dtype)] |
76 |
| - location = 'CellData' |
77 |
| - |
78 |
| - if p_array: |
79 |
| - dataRange = p_array.GetRange(-1) |
80 |
| - nb_comp = p_array.GetNumberOfComponents() |
81 |
| - values = vtk_to_numpy(p_array).ravel() |
82 |
| - js_types = to_js_type[str(values.dtype)] |
83 |
| - location = 'PointData' |
84 |
| - |
85 |
| - # other arrays (points) |
86 |
| - point_data = [] |
87 |
| - for name in point_arrays: |
88 |
| - array = polydata.GetPointData().GetArray(name) |
89 |
| - if array: |
90 |
| - dataRange = array.GetRange(-1) |
91 |
| - nb_comp = array.GetNumberOfComponents() |
92 |
| - values = vtk_to_numpy(array).ravel() |
93 |
| - js_types = to_js_type[str(values.dtype)] |
94 |
| - point_data.append({ |
95 |
| - 'name': name, |
96 |
| - 'values': values, |
97 |
| - 'numberOfComponents': nb_comp, |
98 |
| - 'type': js_types, |
99 |
| - 'location': 'PointData', |
100 |
| - 'dataRange': dataRange, |
101 |
| - }) |
102 |
| - |
103 |
| - # other arrays (cells) |
104 |
| - cell_data = [] |
105 |
| - for name in point_arrays: |
106 |
| - array = polydata.GetCellData().GetArray(name) |
107 |
| - if array: |
108 |
| - dataRange = array.GetRange(-1) |
109 |
| - nb_comp = array.GetNumberOfComponents() |
110 |
| - values = vtk_to_numpy(array).ravel() |
111 |
| - js_types = to_js_type[str(values.dtype)] |
112 |
| - cell_data.append({ |
113 |
| - 'name': name, |
114 |
| - 'values': values, |
115 |
| - 'numberOfComponents': nb_comp, |
116 |
| - 'type': js_types, |
117 |
| - 'location': 'CellData', |
118 |
| - 'dataRange': dataRange, |
119 |
| - }) |
120 |
| - |
121 |
| - state = { |
122 |
| - 'mesh': { |
123 |
| - 'points': points, |
124 |
| - }, |
125 |
| - } |
126 |
| - if len(verts): |
127 |
| - state['mesh']['verts'] = verts |
128 |
| - if len(lines): |
129 |
| - state['mesh']['lines'] = lines |
130 |
| - if len(polys): |
131 |
| - state['mesh']['polys'] = polys |
132 |
| - if len(strips): |
133 |
| - state['mesh']['strips'] = strips |
134 |
| - |
135 |
| - if values is not None: |
136 |
| - state.update({ |
137 |
| - 'field': { |
138 |
| - 'name': field_to_keep, |
139 |
| - 'values': values, |
140 |
| - 'numberOfComponents': nb_comp, |
141 |
| - 'type': js_types, |
142 |
| - 'location': location, |
143 |
| - 'dataRange': dataRange, |
144 |
| - }, |
145 |
| - }) |
146 |
| - |
147 |
| - if len(point_data): |
148 |
| - state.update({ 'pointArrays': point_data }) |
149 |
| - if len(cell_data): |
150 |
| - state.update({ 'cellArrays': cell_data }) |
151 |
| - |
152 |
| - return state |
| 48 | + """Expect any dataset and extract its surface into a dash_vtk.Mesh state property""" |
| 49 | + if dataset is None: |
| 50 | + return None |
| 51 | + |
| 52 | + if point_arrays is None: |
| 53 | + point_arrays = [] |
| 54 | + if cell_arrays is None: |
| 55 | + cell_arrays = [] |
| 56 | + |
| 57 | + # Make sure we have a polydata to export |
| 58 | + polydata = None |
| 59 | + if dataset.IsA("vtkPolyData"): |
| 60 | + polydata = dataset |
| 61 | + else: |
| 62 | + extractSkinFilter = vtkGeometryFilter() |
| 63 | + extractSkinFilter.SetInputData(dataset) |
| 64 | + extractSkinFilter.Update() |
| 65 | + polydata = extractSkinFilter.GetOutput() |
| 66 | + |
| 67 | + if polydata.GetPoints() is None: |
| 68 | + return None |
| 69 | + |
| 70 | + # Extract mesh |
| 71 | + points = b64_encode_numpy(vtk_to_numpy(polydata.GetPoints().GetData())) |
| 72 | + verts = ( |
| 73 | + b64_encode_numpy(vtk_to_numpy(polydata.GetVerts().GetData())) |
| 74 | + if polydata.GetVerts() |
| 75 | + else [] |
| 76 | + ) |
| 77 | + lines = ( |
| 78 | + b64_encode_numpy(vtk_to_numpy(polydata.GetLines().GetData())) |
| 79 | + if polydata.GetLines() |
| 80 | + else [] |
| 81 | + ) |
| 82 | + polys = ( |
| 83 | + b64_encode_numpy(vtk_to_numpy(polydata.GetPolys().GetData())) |
| 84 | + if polydata.GetPolys() |
| 85 | + else [] |
| 86 | + ) |
| 87 | + strips = ( |
| 88 | + b64_encode_numpy(vtk_to_numpy(polydata.GetStrips().GetData())) |
| 89 | + if polydata.GetStrips() |
| 90 | + else [] |
| 91 | + ) |
| 92 | + |
| 93 | + # Extract field |
| 94 | + values = None |
| 95 | + js_types = "Float32Array" |
| 96 | + nb_comp = 1 |
| 97 | + dataRange = [0, 1] |
| 98 | + location = None |
| 99 | + if field_to_keep is not None: |
| 100 | + p_array = polydata.GetPointData().GetArray(field_to_keep) |
| 101 | + c_array = polydata.GetCellData().GetArray(field_to_keep) |
| 102 | + |
| 103 | + if c_array: |
| 104 | + dataRange = c_array.GetRange(-1) |
| 105 | + nb_comp = c_array.GetNumberOfComponents() |
| 106 | + values = vtk_to_numpy(c_array) |
| 107 | + js_types = to_js_type[str(values.dtype)] |
| 108 | + location = "CellData" |
| 109 | + |
| 110 | + if p_array: |
| 111 | + dataRange = p_array.GetRange(-1) |
| 112 | + nb_comp = p_array.GetNumberOfComponents() |
| 113 | + values = vtk_to_numpy(p_array) |
| 114 | + js_types = to_js_type[str(values.dtype)] |
| 115 | + location = "PointData" |
| 116 | + |
| 117 | + # other arrays (points) |
| 118 | + point_data = [] |
| 119 | + for name in point_arrays: |
| 120 | + array = polydata.GetPointData().GetArray(name) |
| 121 | + if array: |
| 122 | + dataRange = array.GetRange(-1) |
| 123 | + nb_comp = array.GetNumberOfComponents() |
| 124 | + values = vtk_to_numpy(array) |
| 125 | + js_types = to_js_type[str(values.dtype)] |
| 126 | + point_data.append( |
| 127 | + { |
| 128 | + "name": name, |
| 129 | + "values": b64_encode_numpy(values), |
| 130 | + "numberOfComponents": nb_comp, |
| 131 | + "type": js_types, |
| 132 | + "location": "PointData", |
| 133 | + "dataRange": dataRange, |
| 134 | + } |
| 135 | + ) |
| 136 | + |
| 137 | + # other arrays (cells) |
| 138 | + cell_data = [] |
| 139 | + for name in point_arrays: |
| 140 | + array = polydata.GetCellData().GetArray(name) |
| 141 | + if array: |
| 142 | + dataRange = array.GetRange(-1) |
| 143 | + nb_comp = array.GetNumberOfComponents() |
| 144 | + values = vtk_to_numpy(array) |
| 145 | + js_types = to_js_type[str(values.dtype)] |
| 146 | + cell_data.append( |
| 147 | + { |
| 148 | + "name": name, |
| 149 | + "values": b64_encode_numpy(values), |
| 150 | + "numberOfComponents": nb_comp, |
| 151 | + "type": js_types, |
| 152 | + "location": "CellData", |
| 153 | + "dataRange": dataRange, |
| 154 | + } |
| 155 | + ) |
| 156 | + |
| 157 | + state = { |
| 158 | + "mesh": {"points": points,}, |
| 159 | + } |
| 160 | + if len(verts): |
| 161 | + state["mesh"]["verts"] = verts |
| 162 | + if len(lines): |
| 163 | + state["mesh"]["lines"] = lines |
| 164 | + if len(polys): |
| 165 | + state["mesh"]["polys"] = polys |
| 166 | + if len(strips): |
| 167 | + state["mesh"]["strips"] = strips |
| 168 | + |
| 169 | + if values is not None: |
| 170 | + state.update( |
| 171 | + { |
| 172 | + "field": { |
| 173 | + "name": field_to_keep, |
| 174 | + "values": b64_encode_numpy(values), |
| 175 | + "numberOfComponents": nb_comp, |
| 176 | + "type": js_types, |
| 177 | + "location": location, |
| 178 | + "dataRange": dataRange, |
| 179 | + }, |
| 180 | + } |
| 181 | + ) |
| 182 | + |
| 183 | + if len(point_data): |
| 184 | + state.update({"pointArrays": point_data}) |
| 185 | + if len(cell_data): |
| 186 | + state.update({"cellArrays": cell_data}) |
| 187 | + |
| 188 | + return state |
153 | 189 |
|
154 | 190 |
|
155 | 191 | def to_volume_state(dataset):
|
156 |
| - '''Expect a vtkImageData and extract its setting for the dash_vtk.Volume state''' |
157 |
| - if dataset is None or not dataset.IsA('vtkImageData'): |
| 192 | + """Expect a vtkImageData and extract its setting for the dash_vtk.Volume state""" |
| 193 | + if dataset is None or not dataset.IsA("vtkImageData"): |
158 | 194 | return None
|
159 | 195 |
|
160 | 196 | state = {
|
161 |
| - 'image': { |
162 |
| - 'dimensions': dataset.GetDimensions(), |
163 |
| - 'spacing': dataset.GetSpacing(), |
164 |
| - 'origin': dataset.GetOrigin(), |
| 197 | + "image": { |
| 198 | + "dimensions": dataset.GetDimensions(), |
| 199 | + "spacing": dataset.GetSpacing(), |
| 200 | + "origin": dataset.GetOrigin(), |
165 | 201 | },
|
166 | 202 | }
|
167 | 203 |
|
168 | 204 | # Capture image orientation if any
|
169 |
| - if hasattr(dataset, 'GetDirectionMatrix'): |
170 |
| - matrix = dataset.GetDirectionMatrix() |
171 |
| - js_mat = [] |
172 |
| - for j in range(3): |
173 |
| - for i in range(3): |
174 |
| - js_mat.append(matrix.GetElement(i, j)) |
| 205 | + if hasattr(dataset, "GetDirectionMatrix"): |
| 206 | + matrix = dataset.GetDirectionMatrix() |
| 207 | + js_mat = [] |
| 208 | + for j in range(3): |
| 209 | + for i in range(3): |
| 210 | + js_mat.append(matrix.GetElement(i, j)) |
175 | 211 |
|
176 |
| - state['image']['direction'] = js_mat |
| 212 | + state["image"]["direction"] = js_mat |
177 | 213 |
|
178 | 214 | scalars = dataset.GetPointData().GetScalars()
|
179 | 215 |
|
180 | 216 | if scalars is not None:
|
181 |
| - values = vtk_to_numpy(scalars).ravel() |
| 217 | + values = vtk_to_numpy(scalars) |
182 | 218 | js_types = to_js_type[str(values.dtype)]
|
183 |
| - state['field'] = { |
184 |
| - 'name': scalars.GetName(), |
185 |
| - 'numberOfComponents': scalars.GetNumberOfComponents(), |
186 |
| - 'dataRange': scalars.GetRange(-1), |
187 |
| - 'type': js_types, |
188 |
| - 'values': values, |
| 219 | + state["field"] = { |
| 220 | + "name": scalars.GetName(), |
| 221 | + "numberOfComponents": scalars.GetNumberOfComponents(), |
| 222 | + "dataRange": scalars.GetRange(-1), |
| 223 | + "type": js_types, |
| 224 | + "values": b64_encode_numpy(values), |
189 | 225 | }
|
190 | 226 |
|
191 |
| - |
192 | 227 | return state
|
193 | 228 |
|
194 | 229 |
|
@@ -383,8 +418,9 @@ def to_volume_state(dataset):
|
383 | 418 | "BlueObeliskElements",
|
384 | 419 | ]
|
385 | 420 |
|
| 421 | + |
386 | 422 | def toDropOption(name):
|
387 |
| - return {'label': name, 'value': name} |
| 423 | + return {"label": name, "value": name} |
388 | 424 |
|
389 | 425 |
|
390 | 426 | preset_as_options = list(map(toDropOption, presets))
|
0 commit comments