Skip to content

Instantly share code, notes, and snippets.

@willstott101
Last active May 28, 2021 18:15
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save willstott101/f3b812f31396e1fd16ac to your computer and use it in GitHub Desktop.
Save willstott101/f3b812f31396e1fd16ac to your computer and use it in GitHub Desktop.
.stl to .obj file converter and slight api.
from struct import unpack
import math
try:
import numpy as np
except ImportError:
print('numpy is required to enable STL transformation.')
class _STLFile(object):
name = None
transformation = None
def __init__(self, filename, name=None, transformation=None):
self.filename = filename
if name is not None:
self.name = name
if transformation is not None:
self.transformation = transformation
def transform_vert(self, vert):
tra = np.array(self.transformation)
vert = np.array([[vert[0]],
[vert[1]],
[vert[2]],
[1]])
vert = tra.dot(vert)
return np.array(vert).flatten()[:-1]
def __iter__(self):
itr = self.read_vectors()
while True:
n = next(itr)
verts = [next(itr), next(itr), next(itr)]
if self.transformation is not None:
verts = [self.transform_vert(v) for v in verts]
yield n, verts[0], verts[1], verts[2]
def read_vectors(self):
raise NotImplementedError()
def to_obj(self, obj, name=None, offset=0):
obj.write('g default\n')
vert_str = 'v {0:6f} {1:6f} {2:6f}\n'
count = 0
for face in self:
obj.write(vert_str.format(*face[1]))
obj.write(vert_str.format(*face[2]))
obj.write(vert_str.format(*face[3]))
count += 3
name = name or self.name or 'default'
obj.write('s 1\ng %s\n' % name)
for i in range(offset, offset + count, 3):
obj.write('f %s %s %s\n' % (i+1, i+2, i+3))
return count
def to_ascii(self, stlfile, name=None):
name = name or self.name or 'default'
stlfile.write('solid %s\n' % name)
for face in self:
stlfile.write('facet normal {:6f} {:6f} {:6f}\n'.format(*face[0]))
stlfile.write('outer loop\n')
stlfile.write('vertex {:6f} {:6f} {:6f}\n'.format(*face[1]))
stlfile.write('vertex {:6f} {:6f} {:6f}\n'.format(*face[2]))
stlfile.write('vertex {:6f} {:6f} {:6f}\n'.format(*face[3]))
stlfile.write('endloop\n')
stlfile.write('endfacet\n')
stlfile.write('endsolid %s\n' % name)
class AsciiSTLFile(_STLFile):
name = None
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.name is None:
self.name = self.read_name()
def read_name(self):
with open(self.filename) as stlfile:
line = next(stlfile)
name = line.split(' ', 1)[1]
return name
def read_vectors(self):
with open(self.filename) as stlfile:
name = self.read_name()
if self.name is None:
self.name = name
line = next(stlfile).strip()
while line.startswith('facet normal'):
# facet normal # # #
yield [float(n) for n in line.split()[-3:]]
next(stlfile) # outer loop
for ___ in range(3): # vertex # # # (x3)
verts = next(stlfile).strip().split()[-3:]
yield [float(v) for v in verts]
next(stlfile) # endloop
next(stlfile) # endfacet
line = next(stlfile).strip()
class BinarySTLFile(_STLFile):
def read_vectors(self):
with open(self.filename, 'rb') as stlfile:
stlfile.seek(80)
length = unpack('<I', stlfile.read(4))[0]
for ___ in range(length):
# Normal, V1, V2, V3
yield unpack('3f', stlfile.read(12))
yield unpack('3f', stlfile.read(12))
yield unpack('3f', stlfile.read(12))
yield unpack('3f', stlfile.read(12))
stlfile.seek(2, 1)
def open_stl(filename, *args, **kwargs):
try:
with open(filename) as stlfile:
word = stlfile.readline(5)
if word == 'solid':
return AsciiSTLFile(filename, *args, **kwargs)
except UnicodeDecodeError:
pass
return BinarySTLFile(filename, *args, **kwargs)
def main():
# http://www.lfd.uci.edu/~gohlke/code/transformations.py.html
from . import transformations as t_
stl = open_stl('cube.stl')
with open('cube.obj', 'w') as obj:
tra = t_.compose_matrix(
scale=[0.5, 0.5, 0.5],
translate=[-2, 1, 2],
angles=[math.pi/4, 0, math.pi/5])
print(tra)
stl.transformation = tra
stl.to_obj(obj)
with open('cube-ascii.stl', 'w') as asciistl:
stl.to_ascii(asciistl)
if __name__ == '__main__':
main()
@Prarthana1116
Copy link

Prarthana1116 commented Sep 21, 2018

If you have time, could you please explain how the code works.Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment