1616
1717from .. import util
1818from ..base import Trimesh
19+ from ..util import comment_strip , decode_text
1920
2021# find the executable for binvox in PATH
2122binvox_encoder = util .which ("binvox" )
2223Binvox = collections .namedtuple ("Binvox" , ["rle_data" , "shape" , "translate" , "scale" ])
2324
25+ _header_required = {"dim" , "translate" , "scale" }
2426
25- def parse_binvox_header (fp ):
27+
28+ def _parse_binvox_header (file_obj ):
2629 """
2730 Read the header from a binvox file.
2831 Spec available:
@@ -48,21 +51,40 @@ def parse_binvox_header(fp):
4851 If invalid binvox file.
4952 """
5053
51- line = fp .readline ().strip ()
52- if hasattr (line , "decode" ):
53- binvox = b"#binvox"
54- space = b" "
55- else :
56- binvox = "#binvox"
57- space = " "
58- if not line .startswith (binvox ):
59- raise OSError ("Not a binvox file" )
60- fp .readline ()
61- shape = tuple (int (s ) for s in fp .readline ().strip ().split (space )[1 :])
62- translate = tuple (float (s ) for s in fp .readline ().strip ().split (space )[1 :])
63- scale = float (fp .readline ().strip ().split (space )[1 ])
64- fp .readline ()
65- return shape , translate , scale
54+ # check for the magic string in the first line
55+ first = decode_text (file_obj .readline ()).strip ()
56+ if "binvox" not in first .lower ():
57+ raise ValueError ("File is not in the binvox format!" )
58+
59+ header = {}
60+ # do a capped iteration
61+ for _ in range (100 ):
62+ # get the line as a lower-case, comment-stripped split list
63+ line = (
64+ comment_strip (decode_text (file_obj .readline ()).lower (), "#" ).strip ().split ()
65+ )
66+ # if the line was a comment or whitespace don't include it
67+ if len (line ) == 0 :
68+ continue
69+
70+ elif line [0 ] == "data" :
71+ # we need to read up until we see "data" so the
72+ # read-the-rest-of-the-payload operation is correct
73+ break
74+
75+ # save the keyed header data
76+ header [line [0 ]] = line [1 :]
77+
78+ if set (header .keys ()) != _header_required :
79+ raise ValueError (
80+ f"Malformed binvox header: `{ header .keys ()} ` != `{ _header_required } `"
81+ )
82+
83+ shape = np .array (header ["dim" ], dtype = np .int64 )
84+ translate = np .array (header ["translate" ], np .float64 )
85+ scale = np .array (header ["scale" ], dtype = np .float64 )
86+
87+ return shape , translate , scale [0 ]
6688
6789
6890def parse_binvox (fp , writeable = False ):
@@ -88,25 +110,17 @@ def parse_binvox(fp, writeable=False):
88110 If invalid binvox file
89111 """
90112 # get the header info
91- shape , translate , scale = parse_binvox_header (fp )
113+ shape , translate , scale = _parse_binvox_header (fp )
92114 # get the rest of the file
93115 data = fp .read ()
94116 # convert to numpy array
95117 rle_data = np .frombuffer (data , dtype = np .uint8 )
118+
96119 if writeable :
97120 rle_data = rle_data .copy ()
98121 return Binvox (rle_data , shape , translate , scale )
99122
100123
101- _binvox_header = """#binvox 1
102- # Generated by trimesh using Patrick Min format ---
103- dim {sx} {sy} {sz}
104- translate {tx} {ty} {tz}
105- scale {scale}
106- data
107- """
108-
109-
110124def binvox_header (shape , translate , scale ):
111125 """
112126 Get a binvox header string.
@@ -123,7 +137,14 @@ def binvox_header(shape, translate, scale):
123137 """
124138 sx , sy , sz = (int (s ) for s in shape )
125139 tx , ty , tz = translate
126- return _binvox_header .format (sx = sx , sy = sy , sz = sz , tx = tx , ty = ty , tz = tz , scale = scale )
140+
141+ return f"""#binvox 1
142+ # generated in `trimesh`
143+ dim { sx } { sy } { sz }
144+ translate { tx } { ty } { tz }
145+ scale { scale }
146+ data
147+ """
127148
128149
129150def binvox_bytes (rle_data , shape , translate = (0 , 0 , 0 ), scale = 1 ):
0 commit comments