Skip to content

Instantly share code, notes, and snippets.

@mtao
Last active April 8, 2019 03:34
Show Gist options
  • Save mtao/7f79e94a2b1cfb864e9aee208d99a2f0 to your computer and use it in GitHub Desktop.
Save mtao/7f79e94a2b1cfb864e9aee208d99a2f0 to your computer and use it in GitHub Desktop.
a simple stl to obj converter. currently doesn't support normals yet
import sys
import numpy as np
from struct import unpack
def strlist_to_vec(arr):
return tuple(float(x) for x in arr)
class Facet:
dtype = np.dtype([
('N', np.float32,(3)),
('U', np.float32,(3)),
('V', np.float32,(3)),
('W', np.float32,(3)),
('A', '<i2',(1,) )
])
def __init__(self,fd, line=None):
self.binary = line is None
if self.binary:
self.parse_binary(fd)
else:
self.parse_ascii(line,fd)
def parse_binary(self,fd):
res = np.fromfile(fd,dtype=Facet.dtype, count=1)
self.normal = res['N'][0,:]
self.vertices = [
tuple(v for v in res['U'][0,:]),
tuple(v for v in res['V'][0,:]),
tuple(v for v in res['W'][0,:])
]
def parse_ascii(self,line,fd):
assert(line[0] == "facet")
assert(line[1] == "normal")
self.vertices = []
self.normal = strlist_to_vec(line[2:])
ol_line = fd.readline().split()
assert(ol_line[0] == "outer")
assert(ol_line[1] == "loop")
while fd:
line = fd.readline().split()
if len(line) == 0:
continue
elif line[0] == "endloop":
break
elif line[0] == "vertex":
self.vertices.append(strlist_to_vec(line[1:]))
def vertex_set(self):
return set(_ for _ in self.vertices)
def as_indices(self,indexer):
return np.array([indexer[v] for v in self.vertices])
pass
class Solid:
def __init__(self,fd, line=None):
self.binary = line is None
if self.binary:
self.parse_binary(fd)
else:
self.parse_ascii(line,fd)
def parse_binary(self,fd):
num_elem = unpack("i",fd.read(4))[0]
self.facets = [Facet(fd) for _ in range(num_elem)]
def parse_ascii(self,line,fd):
assert(line[0] == "solid")
if len(line) > 1:
self.name = line[1]
self.facets = []
while fd:
line = fd.readline().split()
if len(line) == 0:
continue
elif line[0] == "endsolid":
break
elif line[0] == "facet":
self.facets.append(Facet(fd,line))
def vertex_map(self):
index = 0
indexer = {}
for facet in self.facets:
for vertex in facet.vertex_set():
if vertex not in indexer:
indexer[vertex] = index
index+=1
return indexer
def VF(self):
indexer = self.vertex_map()
V = np.zeros((len(indexer),3))
for v,i in indexer.items():
V[i,:] = np.array(v)
F = np.vstack(f.as_indices(indexer) for f in self.facets)
return V,F
def N(self):
return np.vstack(f.normal for f in self.facets)
class STL:
def __is_binary__(self):
with open(self.__filename__,"rb") as fd:
return b'solid' != fd.read(5)
def __init__(self, filename):
self.__filename__ = filename
if self.__is_binary__():
self.parse_binary()
else:
self.parse_ascii()
def parse_binary(self):
with open(self.__filename__,"rb") as fd:
header = fd.read(80)
self.solid = Solid(fd)
def parse_ascii(self):
with open(self.__filename__,"r") as fd:
line = fd.readline().split()
assert(line[0] == "solid")
self.solid = Solid(fd,line)
def VF(self):
return self.solid.VF()
def N(self):
return self.solid.N()
stl_fn = sys.argv[1]
if len(sys.argv) <= 2:
if stl_fn[-4:] == ".stl":
outfile = stl_fn[:-4] + ".obj"
else:
outfile = stl_fn + ".obj"
else:
outfile = sys.argv[2]
print("{} => {}".format(stl_fn,outfile))
stl = STL(stl_fn)
V,F = stl.VF()
#for v in V:
# print("v " +" ".join(map(str,v))+"\n")
#
#for vn in stl.N():
# print("vn " +" ".join(map(str,vn))+"\n")
#
#for i,f in enumerate(F):
# print("f " +" ".join(map(lambda x: "{}/{}".format(x,i+1),f+1))+"\n")
with open(outfile,"w") as fd:
for v in V:
fd.write("v " +" ".join(map(str,v))+"\n")
for vn in stl.N():
fd.write("vn " +" ".join(map(str,vn))+"\n")
for i,f in enumerate(F):
fd.write("f " +" ".join(map(str,f+1))+"\n")
#fd.write("f " +" ".join(map(lambda x: "{}/{}".format(x,i+1),f+1))+"\n")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment