6969
7070import numpy
7171import warnings
72+ from dataclasses import dataclass
7273
7374try :
7475 import openvdb as vdb
7778 vdb = None
7879
7980
81+ @dataclass
82+ class DownCastTo :
83+ gridType : str
84+
85+
8086class 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 ])
0 commit comments