cube <<
Previous Next >> Realizable
Solvespace
https://solvespace.com
https://github.com/solvespace/solvespace
https://github.com/KmolYuan/Pyslvs-UI
將 Solvespace 轉出的 Binary STL 零組件轉為 ASCII STL 程式碼:
import struct
normals = []
points = []
triangles = []
triangle_number = 0
def load_binary_stl(fp):
'''
二位元 STL 檔案格式如下:
檔案標頭共有 80 個字元(bytes), 內容通常省略, 但是內容不可使用 solid, 以免與文字檔案 STL 混淆
UINT8[80] – Header
UINT32 – Number of triangles (I:佔 4 bytes 的 unsigned integer)
foreach triangle
REAL32[3] – Normal vector (f:每一座標分量為一佔 4 bytes 的 float, 共佔 12 bytes)
REAL32[3] – Vertex 1
REAL32[3] – Vertex 2
REAL32[3] – Vertex 3
UINT16 – Attribute byte count (H:兩個 bytes 的 unsigned short, 表示 attribute byte count)
end
'''
# 已經在外部開檔
#fp=open(filename,'rb')
header=fp.read(80)
triangle_number = struct.unpack('I',fp.read(4))[0]
#print(triangle_number)
count=0
while True:
try:
p=fp.read(12)
if len(p)==12:
n=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]]
normals.append(n)
l = len(points)
#print(n)
p=fp.read(12)
if len(p)==12:
p1=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]]
points.append(p1)
#print(p1)
p=fp.read(12)
if len(p)==12:
p2=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]]
points.append(p2)
p=fp.read(12)
if len(p)==12:
p3=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]]
points.append(p3)
triangles.append((l, l+1, l+2))
# 使用 count 來計算三角形平面個數
# triangle_number 為 STL 檔案中的三角形個數
count += 1
#print(count)
# 在前面 12*4 個 bytes 的 normal 與三個點資料後, 為
# 一個 2 bytes 長的 unsigned short, 其值為零, 為 attribute
fp.read(2)
# 讀完所有三角平面後, 即跳出 while
if count > triangle_number:
break
except EOFError:
break
#fp.close()
def read_length(f):
length = struct.unpack("@i", f.read(4))
return length[0]
def read_header(f):
f.seek(f.tell()+80)
def write_as_ascii(outfilename):
f = open(outfilename, "w")
f.write ("solid "+outfilename+"\n")
for n in range(len(triangles)):
f.write ("facet normal {} {} {}\n".format(normals[n][0],normals[n][1],normals[n][2]))
f.write ("outer loop\n")
f.write ("vertex {} {} {}\n".format(points[triangles[n][0]][0],points[triangles[n][0]][1],points[triangles[n][0]][2]))
f.write ("vertex {} {} {}\n".format(points[triangles[n][1]][0],points[triangles[n][1]][1],points[triangles[n][1]][2]))
f.write ("vertex {} {} {}\n".format(points[triangles[n][2]][0],points[triangles[n][2]][1],points[triangles[n][2]][2]))
f.write ("endloop\n")
f.write ("endfacet\n")
f.write ("endsolid "+outfilename+"\n")
f.close()
def main():
infilename = "binary.stl"
outfilename = "ascii.stl"
try:
f = open(infilename, "rb")
#read_header(f)
#l = read_length(f)
try:
load_binary_stl(f)
l = len(normals)
except Exception as e:
print("Exception",e)
print(len(normals), len(points), len(triangles), l)
write_as_ascii(outfilename)
print("done")
except Exception as e:
print(e)
if __name__ == '__main__':
main()
切割多零件 STL 檔案:
stlSplitter.py 與 stlRW.py
stlSplitter.py
#!/usr/bin/env python
# STL splitter
# splits a STL file containing separate objects
#
# copyright 2014 Francesco Santini <francesco.santini@gmail.com>
#
# based on https://github.com/cmpolis/convertSTL by Chris Polis
# and BinarySTL https://github.com/sukhbinder/python by Sukhbinder Singh
#
# Released under the MIT/X license
import stlRW
import sys
from os import path
## functions
# maybe insert a tolerance for floating points?
def check_connection(tri1, tri2):
for v1 in tri1[0:2]:
for v2 in tri2[0:2]:
if (v1[0] == v2[0] and v1[1] == v2[1] and v1[2] == v2[2]): return True
return False
if len(sys.argv) < 2:
print("Usage: " + sys.argv[0] + " <file.stl>")
sys.exit(-1)
fname = sys.argv[1]
print("Reading...")
head,points,n,v1,v2,v3,isAscii = stlRW.stlRead(fname)
print("Analyzing...")
faceTree = []
for triangleIndex in range(0, len(v1)):
triangle = [ v1[triangleIndex], v2[triangleIndex], v3[triangleIndex], n[triangleIndex] ]
connectedTo = []
for treeindex in range(0, len(faceTree)):
for face in faceTree[treeindex]:
if check_connection(face, triangle):
connectedTo.append(treeindex) # the triangle is connected to at least one triangle of the current treeIndex
break
if len(connectedTo) == 0:
# this is a triangle from a new set
#print "new set"
faceTree.append([])
faceTree[len(faceTree)-1].append(triangle)
elif len(connectedTo) == 1:
#print "existing set"
# the triangle is connected to one set
faceTree[connectedTo[0]].append(triangle)
else:
#print "connecting triangle"
#this triangle connects two branches of the tree: collapse the branches
faceTree[connectedTo[0]].append(triangle)
for i in range(len(connectedTo)-1, 0, -1):
faceTree[connectedTo[0]].extend(faceTree.pop(connectedTo[i]))
print("Number of separate objects: ", len(faceTree))
print("Writing files")
origFile, origExt = path.splitext(fname)
for i in range(0, len(faceTree)):
newFile = origFile + "-" + str(i+1) + origExt
print("Writing ", newFile)
n = [field[2] for field in faceTree[i]]
v1 = [field[0] for field in faceTree[i]]
v2 = [field[1] for field in faceTree[i]]
v3 = [field[2] for field in faceTree[i]]
stlRW.stlWrite(newFile, n, v1, v2, v3)
stlRW.py
# STL reader-writer
#
# copyright 2014 Francesco Santini <francesco.santini@gmail.com>
#
# based on https://github.com/cmpolis/convertSTL by Chris Polis
# and BinarySTL https://github.com/sukhbinder/python by Sukhbinder Singh
#
# Released under the MIT/X license
import numpy as np
from struct import unpack, pack
def stlReadBinary(fname):
fp = open(fname, 'rb')
Header = fp.read(80)
nn = fp.read(4)
Numtri = unpack('i', nn)[0]
#print nn
record_dtype = np.dtype([
('normals', np.float32,(3,)),
('Vertex1', np.float32,(3,)),
('Vertex2', np.float32,(3,)),
('Vertex3', np.float32,(3,)) ,
('atttr', '<i2',(1,) )
])
data = np.fromfile(fp , dtype = record_dtype , count =Numtri)
fp.close()
Normals = data['normals']
Vertex1= data['Vertex1']
Vertex2= data['Vertex2']
Vertex3= data['Vertex3']
p = np.append(Vertex1,Vertex2,axis=0)
p = np.append(p,Vertex3,axis=0) #list(v1)
Points =np.array(list(set(tuple(p1) for p1 in p)))
return Header,Points,Normals,Vertex1,Vertex2,Vertex3,False
def stlReadAscii(fname):
fp = open(fname, 'r')
Normals = []
Vertex1 = []
Vertex2 = []
Vertex3 = []
Points = []
while True:
line = fp.readline()
if not line: break
if line.find("solid") > -1 or line.find("endfacet") > -1: continue
if line.find("facet normal") > -1:
normline = line[line.find("facet normal")+len("facet normal"):]
normal = np.array([float(val.strip()) for val in normline.split()])
Normals.append(normal)
vertices = []
fp.readline() # outer loop
# read vertices after normal
for vIndex in range(0,3):
vLine = fp.readline()
vLine = vLine[vLine.find("vertex")+len("vertex"):]
vertices.append(np.array([float(val.strip()) for val in vLine.split()]))
Vertex1.append(vertices[0])
Vertex2.append(vertices[1])
Vertex3.append(vertices[2])
Points.extend(vertices)
fp.readline() # endloop
return "", Points, Normals, Vertex1, Vertex2, Vertex3,True
def stlRead(fname):
fp = open(fname, "r")
try:
if fp.readline().find("solid") > -1:
fp.close()
return stlReadAscii(fname)
else:
fp.close()
return stlReadBinary(fname)
except:
return stlReadBinary(fname)
def stlWriteBinary(fname, normals, v1, v2, v3):
with open(fname, "wb") as fout:
# write 80 bytes header
for i in range(0, 80): fout.write(pack("<c", b" "))
fout.write(pack("<I", len(normals))) # number of triangles
for i in range(0, len(normals)):
fout.write(pack("<fff", *normals[i]))
fout.write(pack("<fff", *v1[i]))
fout.write(pack("<fff", *v2[i]))
fout.write(pack("<fff", *v3[i]))
fout.write(pack("<H", 0)) # attribute
def writeVector(fd, vec):
for v in vec:
fd.write("{:.7e}".format(v))
fd.write(" ")
def stlWriteAscii(fname, normals, v1, v2, v3):
with open(fname, "w") as fout:
fout.write("solid \n")
for i in range(0, len(normals)):
fout.write(" facet normal ")
writeVector(fout, normals[i])
fout.write("\n")
fout.write(" outer loop\n")
fout.write(" vertex ")
writeVector(fout, v1[i])
fout.write("\n")
fout.write(" vertex ")
writeVector(fout, v2[i])
fout.write("\n")
fout.write(" vertex ")
writeVector(fout, v3[i])
fout.write("\n")
fout.write(" endloop\n")
fout.write(" endfacet\n")
def stlWrite(fname, normals, v1, v2, v3, isAscii=False):
if isAscii:
stlWriteAscii(fname, normals, v1, v2, v3)
else:
stlWriteBinary(fname, normals, v1, v2, v3)
# test
if __name__ == "__main__":
import sys
fname = sys.argv[1]
h,p,n,v1,v2,v3,isAscii = stlRead(fname)
print(len(n))
print(v1[0])
stlWriteBinary("binary.stl", n, v1, v2, v3);
stlWriteAscii("ascii.stl", n, v1, v2, v3);
用法:
python stlSplitter.py solvespace_assembly_ascii.stl
目前可以分割從 Solvespace 轉出的組立檔案, 但是各零件比例與座標位置錯誤, 希望修正後可以套用到 wrl 組立件檔案的分割, 以便將 Solvespace 轉出的 wrl 組立檔案輸入 Webots.
Solvespace 轉出的 STL 組件, 利用 stlSplitter.py 轉出各零件 STL 後, 再利用 https://www.patrickmin.com/meshconv/ 轉為 WRL, 之後再將 WRL 零件轉入 Webots.
STL 零件檔案也可以利用 https://github.com/cnr-isti-vclab/meshlab (GUI 以 Qt 編寫) 轉為 WRL, 之後再轉入 Webots.
相關 converter 程式與 meshconv.exe:
meshconv_stl_split_and_converter.7z (for @nfu users only)
可攜 meshlab_portable.7z (for @nfu users only)
Split STL in Javascript: http://mde.tw/cad2019/downloads/splitstl/
Split STL in C++: https://github.com/admesh/stlsplit
STL viewer in C: https://github.com/hroncok/viewstl
Flutter and three.js: https://github.com/andreibosco/flutter_threejs_test
An Optimal Algorithm for 3D Triangle Mesh Slicing.pdf (for @nfu users only)
Libfivepy
https://gitlab.com/rcmz0/libfivepy
https://github.com/mkeeter/fstl
https://github.com/wxkNeter/qt-stl
cube <<
Previous Next >> Realizable