3D列印CLI檔案格式的讀取

樊小白發表於2022-05-04

CLI

英文全稱:COMMON LAYER INTERFACE
中文名稱:通用層介面。
推薦從官方網址看一下其完整的內容:
(https://www.hmilch.net/downloads/cli_format.html)

簡介

通用層介面 (CLI) 是一種通用格式,用於將幾何數​​據輸入到基於層製造技術 (LMT) 的製造系統模型。適用於樹脂的逐層光固化、粉末的燒結或粘合、片材的切割、熔融材料的固化以及任何其他逐層構建模型的系統。

目的

做有關3D列印相關的專案,想從基礎的切偏資料(cli檔案)入手,但是電腦上沒有軟體能開啟cli檔案(開啟之後資料部分會亂碼)。所以蒐集一些資源,希望從cli檔案中獲取切邊的資料資訊。便於後面的學習。(新手學習,前後用了三天,涉及到某些知識比如檔案讀取等一些問題都是現學現用,可能存在一些問題或者更簡單辦法,希望各位大佬能及時指出,共同進步。)

內容

1.格式的解讀,主要是二進位制檔案的解讀
2.python原始碼
3.Java原始碼

格式

有二進位制資料格式與ASCII資料格式,本文主要是介紹二進位制格式,ASCII格式給出官網的例子。兩種格式的異如下:
相同點:二者標頭檔案是相同的。
不同點:ASCII格式有幾何資訊開始標誌,二進位制沒有開始標誌,頭資訊結束之後直接就是資料。

1.ASCII資料格式

下面是官網給的例子
$$HEADERSTART
// This is a example for the use of the Layer Format //
$$ASCII
$$UNITS/1 // all coordinates are given in mm //
// $$UNITS/0.01 all coordinates are given in units 0.01 mm //
$$DATE/070493 // 7. April 1993 //
$$LAYERS/100 // 100 layers //
$$HEADEREND

$$GEOMETRYSTART // start of GEOMETRY-section//
$$LAYER/5.5 // Layer at height z = 5.5 mm//

$$POLYLINE/0,0,5,1.00,2.02,3.30,3.42,5.23,5.01,1.57,5.6,1.00,2.02
$$HATCHES/0,2,10.2,10.4,12.34,12.5,8.8,9.3,15.7,13.2
$$POLYLINE/0,1,10,1.2,4.01,...........
..
..
$$LAYER/5.6
$$POLYLINE/0,0,200,10.23,12.34,..........................
..........
..
..
$$LAYER/15.5
$$POLYLINE/0,0,200,13.23,12.34,..........................
..........
..
..
$$GEOMETRYEND

2.二進位制資料格式

給一個我已經讀出來的例子,例子中是一個簡單的立方體,編號0.01.cli
其中的換行是我人為換的,並不是開始就是這樣。

程式碼

python

此程式碼是我從該網站得來的,非常感謝寶哥的開源。python原始碼
但是由於他的程式碼是一行顯示的,後面我經過排版,修復其中幾個小問題才能跑起來,不過也存在一個問題。直接執行報錯,程式碼為:

byt_int, = struct.unpack("h", byts)

報錯原因是

struct.error: unpack requires a buffer of 2 bytes

但是在報錯地方打上斷點,程式就能執行了,並且能準確讀出資訊,由於我對python程式碼不是很熟悉,簡單搜一下沒有解決這個問題,就暫時先擱置了,如果有大佬知道還望不吝賜教。下面給出具體程式碼。

import struct
class Que:  # 定義一個佇列的類
    def __init__(self):
        self.L = []

    def creat_que(self, num):  # 建立佇列
        for i in range(0, num):
            self.L.append(str(b'x'))  #
            return self.L

    def push(self, item):  # 在末尾增加一個,開頭刪除一個,實現棧操作
        self.L.append(item)
        if self.L.__len__() > 11:
            self.L.pop(0)
        return self.L

    def str_head(self):
        st = [item.replace("b'", '')  # 從連結串列L中將b'刪去
              for item in self.L]
        st = [item.replace("'", '')  # 從連結串列L中將'刪去
              for item in st]
        st = ''.join(st)  # 連線字串
        return st  # 返回一個字串

    def b2int(self):  # 將讀取到的二進位制位元組轉化為 unsign int (2個位元組)
        un_int, = struct.unpack("h", self.L[0])  # h在python中是整型
        return un_int

que_headerend = Que()  # 例項化一個物件來處理$$HEADEREND
que_headerend.creat_que(11)  # 建立一個包含11個元素的佇列,用於判斷是不是頭部資訊結束
que_layer = Que()  # 例項化一個物件來
que_layer.creat_que(2)  # 佇列,用於尋找128/129


class Structure:
    def __init__(self, f_dir, f_w):  # CI,id,dir,n,p1x,p1y,... pnx,pny
        self.UNIT = 0  # 單位
        self.LAYERS = 0  # 多少層
        self.f = f_dir  # 二進位制檔案???
        self.f_ascii = f_w  # 輸出檔案???
        self.head = {}
        self.CI_start = 0  # 128/129???
        self.layer_thick = 0  # 層厚
        self.CI_layer = 0  # 128/129???
        self.id = 0  # 標識
        self.dir = 0  # 順時針還是逆時針???
        self.n = 0  # 點的個數
        self.pn = []  # 座標

    def rep(self, byts):  # 格式替換
        s = str(byts).replace("b", '')
        s = s.replace("'", '')
        return s

    def get_head(self):  # 獲取檔案頭,返回一個字典
        f = self.f  
        byts = ' '  
        d_Ls = []
        L = []

        while byts:  # 開始迴圈讀取二進位制資料
            byts = f.read(1)  #標頭檔案一次讀一個位元組
            que_headerend.push(str(byts))  # 末尾增加一個剛讀到的字元,刪去一個前面的字元
            s = que_headerend.str_head()  # 記住s裡中最多有11個資料
            byt = self.rep(byts)  # 轉換成無符號整型
            L.append(byt)  # L中新增這個資料
            if s == "$$HEADEREND":  # 如果發現了頭結束,說明頭結束了,去得到一些資訊(如層厚等)
                # print("$$HEADEREND FOUND!")
                sL = ''.join(L)  #
                sL = sL.replace("\\n", '')  # 去掉換行符號,得到下面的字串
                '''sL = $$HEADERSTART$$BINARY$$UNITS/00000000.010000$$VERSION/200$$LABEL/1,part1$$DATE/200620$$DIMENSION                
                    /00000072.796799,00000032.592602,00000019.950001,00000132.546799,00000092.342598,00000025.799999                
                    $$LAYERS/000040$$HEADEREND   '''
                Ls = sL.split("$$")  # 用$$分割成單獨的
                ''' Ls = ['', 'HEADERSTART', 'BINARY', 'UNITS/00000000.010000', 'VERSION/200', 'LABEL/1,part1', 'DATE/200620', 
                 'DIMENSION/00000072.796799,00000032.592602,00000019.950001,00000132.546799,00000092.342598,00000025.799999',                 
                 'LAYERS/000040', 'HEADEREND'] '''
                Ls = [s for s in Ls if '/' in s]  # 如果有/就儲存,沒有就刪去
                ''' Ls = ['UNITS/00000000.010000', 'VERSION/200', 'LABEL/1,part1', 'DATE/200620', 'DIMENSION/00000072.796799,00000032.592602,
                00000019.950001,00000132.546799,00000092.342598,00000025.799999', 'LAYERS/000040']  '''
                Ls = [item.split('/') for item in Ls]  # 用/分割成小陣列
                '''  Ls = [['UNITS', '00000000.010000'], ['VERSION', '200'], ['LABEL', '1,part1'], ['DATE', '200620'],                 
                ['DIMENSION', '00000072.796799,00000032.592602,00000019.950001,00000132.546799,00000092.342598,00000025.799999'], 
                ['LAYERS', '000040']]                '''
                d_Ls = dict(Ls)  # 轉化成字典
                '''  d_Ls = {'UNITS': '00000000.010000', 'VERSION': '200', 'LABEL': '1,part1', 'DATE': '200620', 'DIMENSION':                
                 '00000072.796799,00000032.592602,00000019.950001,00000132.546799,00000092.342598,00000025.799999', 'LAYERS': '000040'}  '''
                self.UNIT = float(d_Ls['UNITS'])  # 得到單位
                self.LAYERS = int(d_Ls['LAYERS'])  # 得到層數
                return d_Ls
                break

    def get_layer(self):
        f1 = self.f_ascii
        byts = ' '
        byt = 0
        L = []
        n = 1
        while byts:
            byts = self.f.read(2)  # 因為資料是我無符號整型,所以要一次讀兩個位元組
            byt_int, = struct.unpack("h", byts)  # 進行格式轉換成無符號整型
            if byt_int == 128:  # 如果是128
                f1.write(str(byt_int))  # 寫入128
                f1.write('\n')  # 換行
                byts = self.f.read(2)  # 讀兩個
                byt_int, = struct.unpack("h", byts)  # 轉換格式
                f1.write(str(byt_int * self.UNIT))  # 寫入層厚
                f1.write('\n')
            elif byt_int == 129:  # 寫入129
                f1.write(str(byt_int))
                f1.write('\n')  # 寫入層厚mm
                byts = self.f.read(2)
                byt_int, = struct.unpack("h", byts)
                f1.write(str(byt_int * self.UNIT))
                f1.write('\n')  # 寫入內/外輪廓(0:內輪廓;1:外輪廓)
                byts = self.f.read(2)
                byt_int, = struct.unpack("h", byts)
                f1.write(str(byt_int))
                f1.write('\n')  # 寫入頂點的個數
                byts = self.f.read(2)
                byt_int, = struct.unpack("h", byts)
                f1.write(str(byt_int))
                f1.write('\n')
                m = 2 * byt_int
                while m > 0:
                    byts = self.f.read(2)
                    byt_int, = struct.unpack("h", byts)
                    cod = byt_int * self.UNIT
                    cod = round(cod, 2)
                    f1.write(str(cod))
                    f1.write(',')
                    m -= 1
                    f1.write('\n')
        return 1

#下面要替換成自己的檔案路徑
f = open('D:/py_dir/CLI/tt.cli', 'rb')
f_ascii = open('D:/py_dir/CLI/tt1.cli', 'w')
structure = Structure(f, f_ascii)
d = structure.get_head()
n = structure.get_layer()
layers = structure.LAYERSf.close()
f.closed()  #一定要記得關閉資料
f_ascii.close() #一定要記得關閉資料

java

原始碼在放在我寫的blog裡面了[http://www.welcomefsjblog.top/archives/3d列印cli格式檔案的讀取]

用了差不多一天才寫出來,果然是個小垃圾。期間遇到很多問題,查了一些書或者有些從別人部落格上看的,因為好多所以有些網址都忘了,非常不好意思白嫖了那麼多大佬的解決辦法。
程式碼還是有很多可以優化的地方,因為我的目的也不是得到這個輸出的檔案,而是直接在程式中對資料進行加工處理,在這只是給出程式碼給後面想讀取cli檔案的朋友提供直接的方法,避免走彎路而已。(自己確實走了很多彎路,相關資料不是很多)。
還是推薦各位去看官網的詳細說明,本篇只是用到了無符號整型,以及沒有任何的填充資訊,所以程式碼跑起來不一定能夠成功,只是提供一種思路而已,不至於無從下手。

參考網址

1.cli入門
2.python原始碼
3.鐳射選區熔化分割槽掃描策略演算法生成及軟體系統實現——碩士論文

相關文章