''' Import and export STL files Used as a blender script, it load all the stl files in the scene: blender -P stl_utils.py -- file1.stl file2.stl file3.stl ... ''' import struct import mmap import contextlib import itertools # TODO: endien @contextlib.contextmanager def mmap_file(filename): ''' Context manager over the data of an mmap'ed file (Read ONLY). Example: with mmap_file(filename) as m: m.read() print m[10:50] ''' with open(filename, 'rb') as file: # check http://bugs.python.org/issue8046 to have mmap context # manager fixed in python map = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) yield map map.close() class ListDict(dict): ''' Set struct with order. You can: - insert data into without doubles - get the list of data in insertion order with self.list Like collections.OrderedDict, but quicker, can be replaced if ODict is optimised. ''' def __init__(self): dict.__init__(self) self.list = [] self._len = 0 def add(self, item): ''' Add a value to the Set, return its position in it. ''' value = self.setdefault(item, self._len) if value == self._len: self.list.append(item) self._len += 1 return value def _binary_read(data): # an stl binary file is # - 80 bytes of description # - 2 bytes of size (unsigned int) # - size triangles : # # - 12 bytes of normal # - 9 * 4 bytes of coordinate (3*3 floats) # - 2 bytes of garbage (usually 0) # OFFSET for the first byte of coordinate (headers + first normal bytes) # STRIDE between each triangle (first normal + coordinates + garbage) OFFSET, STRIDE = 84 + 12, 12 * 4 + 2 # read header size, ignore description size = struct.unpack_from('>> tris, pts = read_stl(filename, lambda x:) >>> pts = list(pts) >>> >>> # print the coordinate of the triangle n >>> print(pts[i] for i in tris[n]) ''' tris, pts = [], ListDict() with mmap_file(filename) as data: # check for ascii or binary gen = _ascii_read if data.read(5) == b'solid' else _binary_read for pt in gen(data): # Add the triangle and the point. # If the point is allready in the list of points, the # index returned by pts.add() will be the one from the # first equal point inserted. tris.append([pts.add(p) for p in pt]) return tris, pts.list if __name__ == '__main__': import sys import bpy from io_mesh_stl import blender_utils filenames = sys.argv[sys.argv.index('--') + 1:] for filename in filenames: objName = bpy.path.display_name(filename) tris, pts = read_stl(filename) blender_utils.create_and_link_mesh(objName, tris, pts)