mde.tw

  • Home
    • Site Map
    • reveal
    • blog
  • 程式
    • cp2022
      • cp-syllabus
    • wcm2023
    • cmsimde
      • Portable Python
      • Token and SSH
      • Bootstrap
      • Bugs
      • Frameworks
    • Problem solving
    • Programming
      • Computer
      • Program
      • Python
      • ANSIC
      • Rust
      • Carbon
    • TCExam
      • sendmail
    • Three.js
    • ffmepg
    • Pandoc
    • VSCode
    • Powershell
    • Blockchain
  • Brython
    • Unitconvert
    • Game
    • Simulator
    • Algorithms
  • CPython
    • Pybean
    • PDF
    • RoboDK
    • CAD
      • Python for SW
      • Python for INV
      • Python for NX
    • CAE
    • BS4
    • PostgreSQL
    • PyQt
    • MS Graph
      • MS Teams
  • 設計
    • cad2022
      • cad-syllabus
    • cd2023
    • ME
      • Trends
      • Gears
      • Robots
      • Vehicle
      • Aircraft
      • 3D print
      • Computer Vision
      • Industry 4.0
    • Reference
      • Portable NX1980
      • template and SSH
      • Pull Requests
      • Resolve Conflicts
      • Revealjs
      • Virtualbox
      • cube
    • Solvespace
    • Realizable
    • Bash
    • Leo Editor
    • Fossil SCM
    • Classroom
    • Gazebo
    • Webots
    • Deep RL
  • NX
    • NX1980_setup
    • NX2206
    • NXOpen
    • Mechatronics
  • CoppeliaSim
    • Lua
    • Foosball
    • Examples
      • ZeroMQ
    • Mujoco
    • ROS
  • Projects
    • Wink
    • pjcopsim
      • Copsim Doc
      • Webots Doc
    • pjgazebo
    • pjcontrol
    • pjgithub
    • pjexam
    • pyslvs
    • pjfem
    • pjblender
    • OpenTextbooks
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

Copyright © All rights reserved | This template is made with by Colorlib