• 3D打印CLI文件格式的读取


    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.激光选区熔化分区扫描策略算法生成及软件系统实现——硕士论文

  • 相关阅读:
    只有程序员才能看懂的16张高端漫画
    swift - 如何在数组大小更改后刷新 ForEach 显示元素的数量(SwiftUI、Xcode 11 Beta 5)
    打造高效医患沟通:陪诊小程序开发技术指南
    calico: route (xxx) already exists for an interface other than ‘calicfaxxx1‘ 解决
    企业备份系统运维管理四大关键问题
    SpringAOP(1)-spring源码详解(六)
    Git链接上游仓库
    基本算法-插入排序
    C++项目——集群聊天服务器项目(六)MySQL模块
    Redis 数据结构原理解析
  • 原文地址:https://www.cnblogs.com/fsj1/p/16221073.html