Skip to content

Commit 423c96f

Browse files
committed
Update vtk helper to support binary encoding
1 parent b6f0add commit 423c96f

File tree

1 file changed

+193
-157
lines changed

1 file changed

+193
-157
lines changed

dash_vtk/utils/vtk.py

+193-157
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import dash_vtk
2+
import base64
3+
24
try:
35
# v9 and above
46
from vtkmodules.util.numpy_support import vtk_to_numpy
@@ -11,184 +13,217 @@
1113

1214
# Numpy to JS TypedArray
1315
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",
2426
}
2527

2628

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+
2747
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
153189

154190

155191
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"):
158194
return None
159195

160196
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(),
165201
},
166202
}
167203

168204
# 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))
175211

176-
state['image']['direction'] = js_mat
212+
state["image"]["direction"] = js_mat
177213

178214
scalars = dataset.GetPointData().GetScalars()
179215

180216
if scalars is not None:
181-
values = vtk_to_numpy(scalars).ravel()
217+
values = vtk_to_numpy(scalars)
182218
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),
189225
}
190226

191-
192227
return state
193228

194229

@@ -383,8 +418,9 @@ def to_volume_state(dataset):
383418
"BlueObeliskElements",
384419
]
385420

421+
386422
def toDropOption(name):
387-
return {'label': name, 'value': name}
423+
return {"label": name, "value": name}
388424

389425

390426
preset_as_options = list(map(toDropOption, presets))

0 commit comments

Comments
 (0)