Friday, November 26, 2010

stlascii2bin.py

#  Name:   stlascii2bin.py
#  Author: James Blackwell
#  Rev:    1.0 - 02Oct10 - Created
#
#  Description:
#  This script takes in an ASCII STL file and converts it to a binary
#  version.


import sys, struct


# TODO: 


def CheckASCIIFile(ip):
   '''Check if the first word on this line is 'solid'. If not, it probably
   isn't an ASCII STL file'''
   if not ip.find('solid') == 0:
      # Haven't found the keyword 'solid' on the first line.
      # This isn't an ASCII STL file
      print 'Error: This does not appear to be an ASCII STL file.'
      return False
   return True


def normal(line):
   '''Take a line containing normals and convert to an array of floats'''
   vals = line.split()[-3:]
   norms = [float(n) for n in vals]
#   print "%s %s %s" % (norms[0], norms[1], norms[2])
   return norms
   
def vertex(line):
   '''Take a line containing verticies and convert to an array of floats'''
   vals = line.split()[-3:]
   verts = [float(v) for v in vals]
#   print "%s %s %s" % (verts[0], verts[1], verts[2])
   return verts


def OutputHeader(op, ipfile, opfile):
   '''Create the 80-byte header for the bin STL file'''
   str = 'Converted from ASCII using stlascii2bin.py by James Blackwell. %s to %s' % (ipfile, opfile)
   if len(str)>=80:
      # Too long, strip some off.
      str = str[:80]
   else:
      # Too short, pad it out.
      str = str + (80-len(str))*' '
   op.write(str)
   
def OutputNumTris(op, numTris):
   '''Output the number of triangles in the file'''
   op.write(struct.pack('<L', numTris))
   
def OutputTriangleData(op, triangles):
   '''Output the triangle data (3xNorms, 3x3xVertex, 1xAtribCnt'''
   for triangle in triangles:
      for el in triangle:
         op.write(struct.pack('<f', el))
      op.write(struct.pack('<H', 0))  # Add Attribute Byte Count - Always zero
      
def PrintHelp():
   print '''
   This script converts an ASCII STL file to a binary one.
   
   Useage:
   python stlascii2bin.py <ipfile>
   
   The script will convert the ipfile and generate a binary version with
   the name '<ipfile>_bin.ext'.'''
   
def Main(ipfile):
   triangles = []
   
   # Open the input file or fail gracefully
   try:
      fh = open(ipfile, 'r')
   except:
      print '\n**Error: File "%s" not found**' % (ipfile)
      PrintHelp()
      return


   print 'Converting input file: %s' % (ipfile)
   firstLine = True
   for line in fh:
      if firstLine:
         # Check first line to see if this is an ASCII STL file
         firstLine = False
         if not CheckASCIIFile(line):
            return
      
      # Strip any leading whitespace
      line = line.strip()
      # Handle Normals
      if line.find("facet") == 0:
         triangle = normal(line)


      # Handle Vertexes
      if line.find("vertex") != -1:
         triangle += vertex(line)


      # Finished collating triangle, add it to the list
      if line.find('endfacet') == 0:
         triangles.append(triangle)


   fh.close()
   
   print 'Found %s Triangles' % (len(triangles))
   
   # Create output filename, open it or fail gracefully
   name, ext = ipfile.split('.')
   opfile = name+'_bin'+'.'+ext
   try:
      op = open(opfile, 'wb')
   except:
      print 'Error: Could not create output file %s' % (opfile)
      return
   
   # Output the data
   print 'Generating output file: %s' % (opfile)
   OutputHeader(op, ipfile, opfile)
   OutputNumTris(op, len(triangles))
   OutputTriangleData(op, triangles)
   op.close()
   print 'Conversion complete'
   
if __name__ == '__main__':
   # Check the command line inputs
   if len(sys.argv) != 2:
      PrintHelp()
   else:
      Main(sys.argv[1])

No comments:

Post a Comment