#!/usr/bin/python3 # =============================================================== # Dsplay STL ("StereoLithography") file internals # # Note: the Python struct modules 'f' and 'd' conversion codes # uses the packed representation IEEE 754, binary32 # (for 'f') or binary64 (for 'd') format, regardless # of the floating-point format used by the platform # =============================================================== # # 1. Interpret strings as packed binary data # pydoc-zh.readthedocs.io/en/latest/library/struct.html # # 2. struct documentation # docs.python.org/3/library/struct.htm # # 3. STL (file format) # en.wikipedia.org/wiki/STL_(file_format) # # 4. STLB Files - Stereolithography (binary) # people.sc.fsu.edu/~jburkardt/data/stlb/stlb.html # # 5. FileFormat # docs.fileformat.com/cad/stl/ # # 6. The StL Binary Format # fabbers.com/tech/STL_Format#Sct_binary # # 7. What's inside an .STL? # Edit an .STL file using a text editor! # www.youtube.com/watch?v=7FmW6qhaupw # # =============================================================== import sys import struct from hex_dump import hex_dump import user_interface as ui program_description ='''\ + ----------------------------------------------------+ | test and display binary float data using a STL file | + ----------------------------------------------------+''' # --------------------------------------------------------------- # ---- display vector (3 x 32bit floats) # # ---- Notes: # ---- 1. struct.unpack returns a tuple (get first element) # ---- 2. 'f' is for 32bit 4byte float numbers # ---- 3. 'd' is for 64bit 8byte float numbers # --------------------------------------------------------------- def display_xyz(xyz_byts:bytes,title='xyz') -> None: if len(xyz_byts) != 12: print() print(f'bad length of xyz bytes ({len(byts)})') print() sys.exit() [x] = struct.unpack('f', xyz_byts[0:4]) [y] = struct.unpack('f', xyz_byts[4:8]) [z] = struct.unpack('f', xyz_byts[8:12]) print(f'{title:<8} = {x:>6.02f} {y:>6.02f} {z:>6.02f}') # --------------------------------------------------------------- # ---- display facet # --------------------------------------------------------------- def display_facet(fac_byts:bytearray) -> None: idx = 0 # byte array index display_xyz(fac_byts[idx:idx+12],'norm') idx += 12 display_xyz(fac_byts[idx:idx+12],'v1') idx += 12 display_xyz(fac_byts[idx:idx+12],'v2') idx += 12 display_xyz(fac_byts[idx:idx+12],'v3') # --------------------------------------------------------------- # ---- display STL file # ---- # ---- Note: the STL binary file format uses the IEEE integer # ---- and floating point numerical representation # --------------------------------------------------------------- def display_stl_file(file_path, stl_bytes:bytearray) -> None: title = stl_bytes[0:80].strip().decode('UTF-8') facets = int.from_bytes(stl_bytes[80:84],'little') print(f'file = {file_path}') print(f'title = "{title}"') print(f'facets = {facets}') ##loop = 0 # use for debugging ##loop_count = 2 # use for debugging facet_count = 0 # facet counter facet_offset = 84 # start of facets # (title + facet count) for facit in range(facets): ##loop +=1 # use for debugging ##if loop > loop_count: break # use for debugging facet_count += 1 print(f'------------ facet {facet_count} ------------') display_facet(stl_bytes[facet_offset:facet_offset+50]) facet_offset += 50 return facet_count # --------------------------------------------------------------- # ---- open STL (binary) and read it directly into a byte array # --------------------------------------------------------------- def load_stl_to_array(stl_file_path:str) -> bytearray: with open(stl_file_path,'rb') as file: stl_bytes = file.read() return stl_bytes # --------------------------------------------------------------- # ---- test if the system is 'big' endia or 'little' endia # --------------------------------------------------------------- import sys def test_for_little_endia(): if sys.byteorder=='big': return False return True # --------------------------------------------------------------- # ---- create a string from a byte array # --------------------------------------------------------------- def my_byte_string(byts:bytes) -> None: lst = [] for byt in byts: lst.append('\\x' + f'{byt:02X} ') return ''.join(lst) # --------------------------------------------------------------- # ---- test/display struct float packing and unpacking # --------------------------------------------------------------- def test_struct_float_pack_unpack(): while True: print() s = ui.get_user_input('Enter a float: ') if not s: break tf,flt = ui.is_float(s) if tf is not True: print('OOPS! Try again!') continue print() pflt = struct.pack('f', flt) # pack float print(f'flt type = {type(flt)}') print(f'pflt type = {type(pflt)}') print(f'pflt len = {len(pflt)}') ##print(f'pflt = {pflt}') print(f'pflt = {my_byte_string(pflt)}') ##hex_dump(pflt,0,len(pflt)-1) print() [xflt] = struct.unpack('f', pflt) # unpack float print(f'xflt type = {type(xflt)}') print(f'xflt = {xflt}') sflt = str(xflt) print(f'sflt type = {type(sflt)}') print(f'sflt = {sflt}') # --------------------------------------------------------------- # ---- main # --------------------------------------------------------------- if __name__ == '__main__': # ---- system's endia? print() if test_for_little_endia(): print(f'system is \'little\' endia') else: print(f'system is \'big\' endia') print() print('this code is for \'little\' endia') print('you must modify code for \'big\' endia') print() sys.exit() # ---- system's float max, min, epsilon print() print("Max float value:", sys.float_info.max) print("Min float value:", sys.float_info.min) print("Machine epsilon:", sys.float_info.epsilon) ## ---- test packing floats ## ##test_struct_float_pack_unpack() ##sys.exit() # ---- display program description print() print(program_description) # ---- process STL ("StereoLithography") file file_path = 'stl_binary_cube.stl' # load file bytes into memory stl_bytes = load_stl_to_array(file_path) # ---- display STL file print() facet_count = display_stl_file(file_path,stl_bytes) print() print(f'{facet_count} facets displayed') print()