Я уверен, что есть библиотека Python для загрузки stl-файлов, но я всегда просто писал свой собственный, поскольку формат файла довольно прост (см. Статью Википедии *1002* для описания формата файла).
Вот мой код для чтения файла stl:
import numpy as np
import struct
def Unique(inputList):
"""
Given an M x N list, this function gets the unique rows by treating all
M Ntuples as single objects. This function also returns the indexing
to convert the unique returned list back to the original non-unique list.
"""
hashTable=dict()
indexList=[]
uniqueList=[]
indx=0
for ntuple in inputList:
if not ntuple in hashTable:
hashTable[ntuple]=indx
indexList.append(indx)
uniqueList.append(ntuple)
indx+=1
else:
indexList.append(hashTable.get(ntuple))
return uniqueList, indexList
def IsBinarySTL(filename):
try:
with open(filename,'r') as f:
test=f.readline()
except UnicodeDecodeError:
return True
if len(test) < 5:
return True
elif test[0:5].lower() == 'solid':
return False # ASCII STL
else:
return True
def ReadSTL(filename):
""" Returns numpy arrays for vertices and facet indexing """
def GetListFromASCII(filename):
""" Returns vertex listing from ASCII STL file """
outputList=[]
with open(filename,'r') as f:
lines=[line.split() for line in f.readlines()]
for line in lines:
if line[0] == 'vertex':
outputList.append(tuple([float(x) for x in line[1:]]))
return outputList
def GetListFromBinary(filename):
""" Returns vertex listing from binary STL file """
outputList=[]
with open(filename,'rb') as f:
f.seek(80) # skip header
nFacets=struct.unpack('I',f.read(4))[0] # number of facets in piece
for i in range(nFacets):
f.seek(12,1) # skip normal
outputList.append(struct.unpack('fff',f.read(12))) # append each vertex triple to list (each facet has 3 vertices)
outputList.append(struct.unpack('fff',f.read(12)))
outputList.append(struct.unpack('fff',f.read(12)))
f.seek(2,1) # skip attribute
return outputList
if IsBinarySTL(filename):
vertexList = GetListFromBinary(filename)
else:
vertexList = GetListFromASCII(filename)
coords, tempindxs = Unique(vertexList)
indxs = list()
templist = list()
for i in range(len(tempindxs)):
if (i > 0 ) and not (i % 3):
indxs.append(templist)
templist = list()
templist.append(tempindxs[i])
indxs.append(templist)
return np.array(coords), np.array(indxs)
А вот код для вычисления фасетных нормалей (при условии правила правой руки)
def GetNormals(vertices, facets):
""" Returns normals for each facet of mesh """
u = vertices[facets[:,1],:] - vertices[facets[:,0],:]
v = vertices[facets[:,2],:] - vertices[facets[:,0],:]
normals = np.cross(u,v)
norms = np.sqrt(np.sum(normals*normals, axis=1))
return normals/norms[:, np.newaxis]
Наконец, код для записи файла stl (с учетом списка атрибутов для каждого аспекта):
def WriteSTL(filename, vertices, facets, attributes, header):
"""
Writes vertices and facets to an stl file. Notes:
1.) header can not be longer than 80 characters
2.) length of attributes must be equal to length of facets
3.) attributes must be integers
"""
nspaces = 80 - len(header)
header += nspaces*'\0'
nFacets = np.shape(facets)[0]
stl = vertices[facets,:].tolist()
with open(filename,'wb') as f: # binary
f.write(struct.pack('80s', header.encode('utf-8'))) # header
f.write(struct.pack('I',nFacets)) # number of facets
for i in range(nFacets):
f.write(struct.pack('fff',0,0,0)) # normals set to 0
for j in range(3):
f.write(struct.pack('fff',stl[i][j][0], stl[i][j][1], stl[i][j][2])) # 3 vertices per facet
f.write(struct.pack("H", attributes[i])) # 2-byte attribute
Собрав все это вместе, вы можете сделать что-то вроде следующего:
if __name__ == "__main__":
filename = "bunny.stl"
vertices, facets = ReadSTL(filename) # parse stl file
normals = GetNormals(vertices, facets) # compute normals
# Get some value related to normals
attributes = []
for i in range(np.shape(normals)[0]):
attributes.append(int(255*np.sum(normals[i])**2))
# Write new stl file
WriteSTL("output.stl", vertices, facets, attributes, "stlheader")
этот фрагмент кода читает файл stl, вычисляет нормали, а затем назначает значение атрибута на основе квадрата суммы каждой нормали (обратите внимание, что атрибут должен быть целым числом).
Вход и выход этого скрипта выглядят следующим образом: