Skip to content

Commit acff126

Browse files
committed
Updated the OpenVDB.py with new API structure
1 parent 2c75469 commit acff126

2 files changed

Lines changed: 120 additions & 25 deletions

File tree

gridData/OpenVDB.py

Lines changed: 88 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969

7070
import numpy
7171
import warnings
72+
from dataclasses import dataclass
7273

7374
try:
7475
import openvdb as vdb
@@ -77,6 +78,11 @@
7778
vdb = None
7879

7980

81+
@dataclass
82+
class DownCastTo:
83+
gridType: str
84+
85+
8086
class OpenVDBField(object):
8187
"""OpenVDB field object for writing volumetric data.
8288
@@ -153,10 +159,12 @@ def __init__(
153159

154160
if grid is not None:
155161
self._populate(grid, origin, delta)
162+
self.vdb_grid = self._create_openvdb_grid()
156163
else:
157164
self.grid = None
158165
self.origin = None
159166
self.delta = None
167+
self.vdb_grid = None
160168

161169
def _populate(self, grid, origin, delta):
162170
"""Populate the field with grid data.
@@ -206,31 +214,70 @@ def _populate(self, grid, origin, delta):
206214
"delta must be either a length-3 vector or a 3x3 diagonal matrix"
207215
)
208216

209-
def write(self, filename):
210-
"""Write the field to an OpenVDB file.
211-
212-
Parameters
213-
----------
214-
filename : str
215-
Output filename (should end in .vdb)
217+
def _get_best_grid_type(self):
218+
"""Selects the suitable OpenVDB grid type
216219
217-
Notes
218-
-----
219-
Limitations in OpenVDB can lead to loss of precision. If the input
220-
data is not of type float32, it will be converted to FloatGrid which is float32.
220+
Returns
221+
-------
222+
openvdb.GridBase
221223
224+
Raises
225+
------
226+
TypeError
227+
If dtype is not supported or no suitable grid type is available
222228
"""
229+
datatypes = {
230+
numpy.dtype("bool"): ["BoolGrid"],
231+
numpy.dtype("int8"): [DownCastTo("Int32Grid"), DownCastTo("FloatGrid")],
232+
numpy.dtype("uint8"): [DownCastTo("Int32Grid"), DownCastTo("FloatGrid")],
233+
numpy.dtype("int16"): [DownCastTo("Int32Grid"), DownCastTo("FloatGrid")],
234+
numpy.dtype("uint16"): [DownCastTo("Int32Grid"), DownCastTo("FloatGrid")],
235+
numpy.dtype("int32"): ["Int32Grid", DownCastTo("FloatGrid")],
236+
numpy.dtype("uint32"): ["Int32Grid", DownCastTo("FloatGrid")],
237+
numpy.dtype("int64"): ["Int64Grid", DownCastTo("FloatGrid")],
238+
numpy.dtype("uint64"): ["Int64Grid", DownCastTo("FloatGrid")],
239+
numpy.dtype("float16"): [DownCastTo("HalfGrid"), DownCastTo("FloatGrid")],
240+
numpy.dtype("float32"): ["FloatGrid"],
241+
numpy.dtype("float64"): ["DoubleGrid", DownCastTo("FloatGrid")],
242+
}
243+
244+
try:
245+
vdb_gridtypes = datatypes[self.grid.dtype]
246+
except KeyError:
247+
raise TypeError(f"Data type {self.grid.dtype} not supported for VDB")
248+
249+
for gridtype_downcast in vdb_gridtypes:
250+
if isinstance(gridtype_downcast, DownCastTo):
251+
gridtype = gridtype_downcast.gridType
252+
else:
253+
gridtype = gridtype_downcast
223254

224-
if self.grid.dtype == numpy.bool_ or self.grid.dtype == bool:
225-
vdb_grid = vdb.BoolGrid()
226-
use_tolerance = False
227-
228-
else:
229-
vdb_grid = vdb.FloatGrid()
230-
if self.tolerance == None or self.tolerance == 0:
231-
use_tolerance = False
255+
try:
256+
VDB_Grid = getattr(vdb, gridtype)
257+
except AttributeError:
258+
continue
232259
else:
233-
use_tolerance = True
260+
if isinstance(gridtype_downcast, DownCastTo):
261+
warnings.warn(
262+
f"Grid type {vdb_gridtypes[0]} not available. Using {gridtype} instead. Data may lose precision.",
263+
UserWarning,
264+
)
265+
return VDB_Grid()
266+
267+
raise TypeError(
268+
f"Could not find any VDB grid type for numpy dtype {self.grid.dtype}"
269+
)
270+
271+
def _create_openvdb_grid(self):
272+
"""Create and populate an OpenVDB grid
273+
274+
Returns
275+
-------
276+
openvdb.GridBase
277+
278+
"""
279+
280+
vdb_grid = self._get_best_grid_type()
234281

235282
vdb_grid.name = self.name
236283

@@ -245,11 +292,27 @@ def write(self, filename):
245292
except (TypeError, ValueError) as e:
246293
warnings.warn(f"Could not set metadata '{key}': {e}", UserWarning)
247294

248-
if use_tolerance:
249-
vdb_grid.copyFromArray(self.grid, tolerance=self.tolerance)
250-
vdb_grid.prune()
251-
else:
295+
if isinstance(vdb_grid, vdb.BoolGrid) and (
296+
self.tolerance is None or self.tolerance == 0
297+
):
252298
vdb_grid.copyFromArray(self.grid)
253299
vdb_grid.prune(tolerance=False)
300+
else:
301+
if self.tolerance is None:
302+
vdb_grid.copyFromArray(self.grid)
303+
else:
304+
vdb_grid.copyFromArray(self.grid, tolerance=self.tolerance)
305+
306+
vdb_grid.prune()
307+
308+
return vdb_grid
254309

255-
vdb.write(filename, grids=[vdb_grid])
310+
def write(self, filename):
311+
"""Write the field to an OpenVDB file.
312+
313+
Parameters
314+
----------
315+
filename : str
316+
Output filename (should end in .vdb)
317+
"""
318+
vdb.write(filename, grids=[self.vdb_grid])

gridData/tests/test_vdb.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,38 @@ def test_write_vdb_with_tolerance_parameter(self, tmpdir, grid345):
272272
assert acc.getValue((0, 0, 0)) == pytest.approx(float(data[0, 0, 0]))
273273
assert acc.getValue((1, 2, 3)) == pytest.approx(float(data[1, 2, 3]))
274274

275+
def test_write_vdb_float64_conversion_warning(self, tmpdir):
276+
data = np.random.random((5, 5, 5))
277+
278+
g = Grid(data, origin=[0, 0, 0], delta=[1, 1, 1])
279+
outfile = str(tmpdir / "float64.vdb")
280+
281+
with pytest.warns(
282+
UserWarning, match="Grid type DoubleGrid not available.*FloatGrid"
283+
):
284+
g.export(outfile)
285+
286+
grids, _ = vdb.readAll(outfile)
287+
assert isinstance(grids[0], vdb.FloatGrid)
288+
289+
def test_write_vdb_int32_conversion_warning(self, tmpdir):
290+
data = np.arange(27, dtype=np.int32).reshape((3, 3, 3))
291+
292+
g = Grid(data, origin=[0, 0, 0], delta=[1, 1, 1])
293+
outfile = str(tmpdir / "int32.vdb")
294+
295+
with pytest.warns(
296+
UserWarning, match="Grid type Int32Grid not available.*FloatGrid"
297+
):
298+
g.export(outfile)
299+
300+
grids, _ = vdb.readAll(outfile)
301+
assert isinstance(grids[0], vdb.FloatGrid)
302+
303+
acc = grids[0].getAccessor()
304+
assert acc.getValue((0, 0, 0)) == pytest.approx(float(data[0, 0, 0]))
305+
assert acc.getValue((1, 1, 1)) == pytest.approx(float(data[1, 1, 1]))
306+
275307

276308
@pytest.mark.skipif(
277309
not HAS_OPENVDB, reason="Need openvdb to test import error handling"

0 commit comments

Comments
 (0)