CuraEngine三维切片源码编译与解读

cygwin 编译 CuraEngine

说明

鉴于很多筒子们都开始用 cygwin/mingw 来编译 CuraEngine,以及用 VS 编译 CuraEngine 的各种问题,尝试了用 cygwin64 来编译。
用 unix 编译环境在 windows 下编译 CuraEngine 的好处如下:

  1. 基本不需要修改源码。
  2. 可以随时与官网库同步。
  3. VS 收费(当然这不算好处)

几个注意点

  1. 本教程用 cygwin 编译 CuraEngine,还是非常顺利的,比 VS 要简单很多。
  2. 有人觉得 mingw 也可以用来编译 CuraEngine,这里并不推荐。因为 mingw 的 gcc 版本目前只到 4.8.1(至少我下载的版本最高只有 4.8.1),而 CuraEngine 的最新版貌似用到了 4.9,我就不担保不出现什么诡异的问题了。
    如果一定要用 mingw 的话,切记要用 mingw 的 POSIX 版本,用 POSIX 版本,用 POSIX 版本(重要的事情说三遍),为什么呢?因为在 mingw 的其他版本中对 thread 的库不完全支持,mutex 文件使用时会出错。而 libArcus 正好用到了这个(不要问我为什么会知道。。。)。
  3. cygwin 现在最新版好像已经支持中文了,不过最好还是将 cygwin 安装在英文目录下,将环境变量添加进去。安装盘空间需要足够大,我安装了包含所有组件的 cygwin64 用了 50 多 GB(当然编译 curaengine 不需要也不建议完整安装,具体需要装哪些插件参照群里文档)。
  4. 测试环境 win10 64 位中文专业版,DELL inspiron 17R,你可能需要额外安装 perl,cmake,python,git 等(我的之前就有)。

推荐软件

  1. cygwin 我用的是下面版本,最新 gcc 已经到了 4.9.3  cygwin版本.png\\更新我选的是 from internet and install,学校的教育网网速比较快。
  2. cmake https://cmake.org/ ,版本越新越好
  3. ConEmu http://www.softpedia.com/get/System/System-Miscellaneous/ConEmu.shtml
    【以下软件不必须】 模拟了 windows 下终端的功能,可以在里面运行 cygwin。
  4. Wox http://www.getwox.com/ 类似于 mac os 下的 Alfred,用来快速启动程序。
  5. spacemacs! https://github.com/syl20bnr/spacemacs 极力推荐。emacser 和 vimer 都会喜欢的。之前用 vs 版本的 cuaengine 因为一直找不到一个顺手的 IDE,现在已经没有任何理由用 VS 了。 spacemacs 是 emacs+一个完美的配置文件。emacs 入门门槛较高,但是一旦使用会受益终生。
  6. Zeal http://zealdocs.org/ 类似于 mac os 下地 Dash,可以配合 spacemacs 查看说明文档。跟 vs 的 f1 键功能差不多哦呵呵。

编译 CuraEngine

说明

https://github.com/Ultimaker/CuraEngine 其实就是按照官方的说明一步一步来,期间需要自己改正一些错误(由于 cygwin 没能完全模拟 unix 环境)

编译 protobuf

git clone https://github.com/google/protobuf.git #下载 protobuf 库
uncomment line 19 to line 38 #注释掉 19 行到 38 行(可能行号有些变动),去掉对 gtest 的依赖
./autogen.sh

proto_autogen.png

./configure

ptoto_configure.png

make

proto_make.png

make install

proto_makeinstall.png

编译 libArcus 库

git clone https://github.com/Ultimaker/libArcus.git
mkdir build && cd build
cmake ..

libArus_cmake.png

make

libArcus_make.png

make install  默认装在/usr/local 下面

libArcus_makeinstall.png

编译 CuraEngine 库

git clone https://github.com/Ultimaker/CuraEngine.git
mkdir build && cd build
cmake ..
make

CuraEngine_build.png

以上图片是最终编译成功以后的结果。make 过程中有几处错误,需要自己修改 CuraEngine 的源码。

  1. commandSocket.cpp
    if (setting.name() == "extruder_nr")   //192 行
            {
                extruder_train_nr = std::stoi(setting.value());
                break;
            }
    

    改成:

    if (setting.name() == "extruder_nr")   
    {
       std::string temp = setting.value();
       extruder_train_nr = strtol(temp.c_str(),0,10);
    }
    

    commandSocket纠错1.png
    并且#include <stdlib.h>

  2. settingRegistry.cpp
    SettingConfig& child = category_trains.getOrCreateChild(std::to_string(train_nr), std::string("Extruder ") + std::to_string(train_nr));  //205 行
    

    改成:

    std::ostringstream os;
    os << train_nr;
     SettingConfig& child = category_trains.getOrCreateChild(os.str(), std::string("Extruder ") + os.str());
    

    settingRegistry纠错.png

  3. main.cpp
    port = std::stoi(ip_port.substr(ip_port.find(':') + 1).data());  //77 行
    

    改成:

    std::string temp = ip_port.substr(ip_port.find(':') + 1).data();
    port = strtol(temp.c_str(),0,10);
    

    纠错main.png

运行  CuraEngine.exe

1、通过cygwin的环境运行。在cygwin环境下,curaengine的build目录输入

./CuraEngine  [参数表]

2、在windows环境下运行。

需要先拷贝\cygwin64\usr\local\bin里面的cygArcus-1.dll、cygprotobuf-10.dll ,和        \cygwin64\bin里面的cyggcc_s-seh-1.dll、cygstdc++-6.dll、cygwin1.dll、cygz.dll到CuraEngine.exe所在的目录下。这样就可以用windows下的命令行运行了。命令:

CuraEngine.exe [参数表]

 

最后

这里再次推荐用 spacemacs+cedet 来阅读代码。

spacemacs阅读代码

发表在 CuraEngine小白学习笔记| 5条评论

平面多边形格式 polygon 和 clipper 库

clipper 库

概述

clipper 是一个提供图形切片(clipping)和偏移(offse)的二维图形库。切片类型包括交(intersection)、和(Union)、差(difference)、异或(xor)运算。网页文档见http://www.angusj.com/delphi/clipper/documentation/Docs/Overview/_Body.htm 。
由于考虑到浮点数会截断产生偏差,clipper 库采用整形数据,可以包括 32 位整数类型或者 long long 类型(默认)。

数据类型

clipping 概念。

  • 通过

path(contour)

  • 指的是一组有序的顶点,组成了未封闭路径(open path)或者闭合路径。

Polygon

  • 在平面内一个闭合的不自交的轨迹。不自交指的是路径轨迹上的任意线段不相交。

PolyNode、Polytree

  • 形成了树状结构,包含多边形之间的关系。
  • 属性
    • Contour :Path 类型,轮廓
  • polytree 结构
    polytree: 
       Contour = ()
       ChildCount = 1
       Childs[0]: 
           Contour = ((10,10),(100,10),(100,100),(10,100))
           IsHole = False
           ChildCount = 1
           Childs[0]: 
               Contour = ((20,20),(20,90),(90,90),(90,20))
               IsHole = True
               ChildCount = 2
               Childs[0]: 
                   Contour = ((30,30),(50,30),(50,50),(30,50))
                   IsHole = False
                   ChildCount = 0
               Childs[1]: 
                   Contour = ((60,60),(80,60),(80,80),(60,80))
                   IsHole = False
                   ChildCount = 0
    

    polytree.png

hole

  • 是封闭的轨迹。内部不标记填充。

Polygon filling rule

  • 对任意一组闭合多边形,指定哪些需要进行填充。

ClipperBase 类

  • 是 Clipper 类的抽象基类。包括了 AddPath,Clear,GetBounds(返回所有被切片 object 的范围矩形)

Clipper 类

  • 包括了在 polygons 上的布尔操作(交、和、差、异或)。
  • 属性
    • preserveCollinear
      • 对在同一条线段上的点是否进行简化处理。
    • reverseSolution
      • 跟切片后路径的方向相关。
    • StrictlySimple
      • simple polygon 是不自交的。
      • weakly simple polygon 包括了重合的顶点或者接触的边线。
      • strictly simple polygon 不包括重合或者接触的顶点、边线。
    • ZFullFunction
      • 只有当 use_xyz 宏定义时才有效。这里的 z 不表示空间的 z 坐标 !。在 intPoint 结构中用户可以储存自定义值。
  • 方法
    • 构造函数
      • clipper(int initOptions=0)
      • initOptions 包括了 ioReverseSoluton=1,ioStrictlySimple=2,inPreserveCollinear=4 选项。
    • Execute
      bool Execute(ClipType clipType,
        Paths &solution,      //切片的结果
        PolyFillType subjFillType = pftEvenOdd, //
        PolyFillType clipFillType = pftEvenOdd);
      
      bool Execute(ClipType clipType,
        PolyTree &solution,
        PolyFillType subjFillType = pftEvenOdd,
        PolyFillType clipFillType = pftEvenOdd);
      
      • PolyFillType 指定了对路径的填充方法。

        • 用 winding Number 方法,如下图。winding number 的初值为零。对于一组轮廓,从外面向里走,路过一条路径,根据路径的方向,对 winding number 进行自增或者自减操作。由此每条路径都有了一个 number 值。

        winding_number.png

        • 由此引出四种填充方法。
          • even-odd filling :顾名思义,对奇数的路径标记填充,对偶数则标记不填充。
          • Non-Zero filling:对不为零的进行填充。
          • positive:正的进行填充。
          • negative:负的子区域进行填充。
      • clipType 对应了布尔运算方式。

clpperoffset 类

  • 属性
    • ArcTolerance: 圆角的精度。
    • MiterLimit: 为了防止尖角处延伸过大,这个参数指定最大的延伸量。
  • joinType
    • 路径偏移后的形状类型。直接上图。

    jointypes.png

endtype

endtypes1.png endtypes2.png


polygon

概述

  • polygon 等类对 clipper 进行了封装,使其更好得满足二维平面上的运算操作。

polygonType

  • 包括了 NoneType、Inset0Type、InsetXType、SkinType、SupportType、SkirtType。

PolygonRef 类

  • Polygon 类的基类。方法根据名字都猜得出来。
unsigned int size() const;
int64_t polygonLength() const;//多边形的周长
void translate(Point translation)//多边形在平面上根据 translation 矢量偏移量。
bool inside(Point p, bool border_result=false)//点 p 是否在区域内。返回是否、或者 border_result(即在边界上)
void smooth(int remove_length, PolygonRef result) //当小于 remove_length 时将点从多边形中移除。
    void simplify(int allowed_error_distance_squared, PolygonRef result) //如果三点几乎呈一条直线,将中间点移除

Polygon 类

  • 包含了一个 ClipperLib::Path 类的成员 ploy。

Polygons 类

  • 包含了一个 ClipperLib::Paths 类的成员 ploygons。
  • 交、和、差、异或、偏移运算的封装。
  • 对一组 polygon 的 smooth、simplify。
  • 根据 even-odd 填充原则分组,所得每个 polygons 中的第一个 polygon 为 outline,其他都是 holes。
std::vector<PolygonsPart> splitIntoParts(bool unionAll = false) const;

PolygonsPart

  • 包含 holes 的区域。第一个 polygon 是 outline,其他的都是 holes 在 outline 内。

AABB 类

  • 一个多边形在平面区域内范围的矩形。
发表在 CuraEngine小白学习笔记| 留下评论

切片算法(3)–生成平面数据!

承接上一篇:

  • FffPolygonGenerator 类中 ,
  • sliceModel 方法调用了 Slicer 切片,调用了 CreateLayParts 转换成 平面封闭多边形(此时的多边形只是孤立的,相互没有关系)。
  • 用 createLayerParts 方法将平面封闭多边形转换成 LayerParts。
  • 再通过 slicers2polygons 方法,先将为空的开头几层移除-> 处理 insets(processInsets)->处理溢出保护层(processOozeShield)-> 处理 support(generateSupportAreas) ->处理 skins(processSkins,combineSparseLayers 合并稀疏层)-> 处理 wipeTower(processWipeTower) -> 处理平台附着(processPlatformAdhesion)
    • processInsets 调用了 generateInsets
    • processSkins 调用了 generateSkins,generateSpares,generatePerimeterGaps
    • processPlatformAdhesion 调用了 generateSkirt,generateRaft

Slicer 类

概述

  • The Slicer creates layers of polygons from an optimized 3D model. The result of the Slicer is a list of polygons without any order or structure.即生成的多边形是没有顺序和结构的。

原理

optimizedPolygons 方法

  • 在一个多边形中将距离过近的点和几乎呈一条直线的三个点移除。将点数小于 3 个的多边形移除。

multiVolumes

  • 用来将从 multiple volumes 切片形成的 layers 进行修复和改变。
  • carveMultipleVolumes 方法(未使用)
    • 去除 volume 中重叠的部分。
  • generateMultipleVolumeOverlap 方法
    • 使不同 volume 中得 parts 产生少许重叠,得到更好的连接。

LayerPart 类

概述

  • layer-part 产生的步骤是实际产生 3D 打印数据的第一步。把 slice 得到的无序的多边形变成一组“part”,不过仍然有可能有孔洞。part 中所有的 element 在移动到下一层之前都会被打印。

createLayerWithParts

  • createLayerWithParts(SliceLayer& storageLayer, SliceLayer* layer,bool union_layers, bool union_all_remove_holes)
  • 将一层的的切片 layer 的数据整合到 storage 中。

createLayerParts

  • 对每层调用 createLayerWithParts 方法,得到有序的 layer。并且每层整合完成后显示 layerparts +层数。

dumpLayerparts

  • 生成 svg(二维矢量图形格式)文件。

inset

概述

  • 只包含了处理 insets 的方法。
void generateInsets(SliceLayerPart* part, int extrusionWidth, int insetCount, bool avoidOverlappingPerimeters); 
void generateInsets(SliceLayer* layer, int extrusionWidth, int insetCount, bool avoidOverlappingPerimeters);
发表在 CuraEngine小白学习笔记| 留下评论

切片算法(2)–切片数据存贮类

PrintObject 类

  • 一个 PrintObhect 包含有一个或者多个 3D Mesh 对象。再构造函数中,传递进了 SettingBase 对象的信息。
  • 通过 fffProcessor 的 processFile 方法,将 stl 文件面片数据传入 PrintObject 的 meshes 中。

公有方法

void offset (Point3 offset) ;//将 meshes 中的所有顶点偏移 offset 量。即整个打印模型相对于平台移动
void finalize(); 
bool loadMeshFromFile(PrintObject* object,const char* filename,FMatrix3x3& matrix);//从文件导入模型,存于 object 中。
  • 其中,在 finalize 中,判断是否有 mesh_position_x/y/z 的键值,如果有说明 mesh 的位置已经给出,需要通过偏移一定量将 mesh 放置到 3d 空间中的合适位置。
  • finalizez 之后判断 machine_center_is_zero,并且偏移(machine_width,machine_depth,0)使打印物体中心与热床中心重合。

fffProcessor 类

  • 是总的处理 PrintObject 文件的类。

    FffPolygonGenerator polygonGenerator; //多边形生成器
    FffGcodeWriter gcodeWriter;   //gcode 生成器
    TimeKeeper timeKeeper;    //时间记录器
    CommandSocket* commandSocket;  //通信器
    

processModel

  • 是核心函数。有一个计时对象,整个过程结束后显示 total time elapsed … 切片时间。
  • 判断配置参数 wireframe_enabled(线框) 的值:
    • 如果为真,执行 Neith/Weaving/WirePrinting/Webbd printing,用到 Weaver 类和 Wireframe2gcode 类。
    • 如果为假,调用 FffPolygonGenerator 类的对象 polygonGenerator 进行切片,将切片后数据存入 SliceDataStorage 对象 storage 中,再转换成 gcode。

FffPolygonGenerator 类

  • 在这里生成了多边形。 模型被切片,每片由代表轮廓的多边形组成,——轮廓包括模型的内部和外部的边界。
  • 切片后,layers 产生。比如 wall insets 围墙 ,要填充为 support 的区域,稀疏填充的区域。这里都由 polygon 表示了。

    bool generateAreas(SliceDataStorage& storage, PrintObject* object, TimeKeeper& timeKeeper);//由 outline 生成 polygons,包括 inset,support 等
    void sendPolygons(PolygonType type, int layer_nr, Polygons& polygons);//向 commandSocker 发送多边形所属类型
    bool sliceModel(PrintObject* object, TimeKeeper& timeKeeper, SliceDataStorage& storage);//切片生成 outline
    void slices2polygons(SliceDataStorage& storage, TimeKeeper& timeKeeper);//操作生成的 inset ,support 多边形。
    void processInsets(SliceDataStorage& storage, unsigned int layer_nr);//操作由 wall 外墙 outline 生成的 inset 多边形。
    void processOozeShield(SliceDataStorage& storage, unsigned int totalLayers);//生成渗出保护?
    void processSkins(SliceDataStorage& storage, unsigned int layer_nr); //操作某层的 skin(即需要被完全填充的部分)
    void processWipeTower(SliceDataStorage& storage, unsigned int totalLayers);//操作生成的 wipe tower 多边形,wipeTower 是一个 tower 区域,在那 喷头可以清理自己或者换喷头。
    void processPlatformAdhesion(SliceDataStorage& storage); //处理平台附着的结构
    

sliceModel 函数

  • 在 sliceModel 函数中,首先对用 Slicer 对 meshes 进行切片。输出切片层数 layer count,切片时间 sliced model in … 。之后,meshes 中数据就存在 slicerlist 中。由于此时 meshes 已经没用了,可以 clear 掉。
    再通过 createLayerParts 方法将 sliceList 中数据转变成 meshStorage 中的 layParts。(说明:layer-part 是生成实际数据的第一步。它用到了 slicer 之后的一堆没有顺序的多边形数据,把他们组成有序的组(即 part),parts 则代表在 2D 层中可能有孔洞的孤立区域。
  • LayerParts 是需要掌握的一个重要概念。LayerParts 是一个单独的 layer 中的一部分。比如,切割一个正方体,我们只会得到一个 LayerPart,但是,如果切割一个桌子,这个桌子有四个脚,我们就会得到四个切面,也就是四个 LayerPart,我们把这在同一平面的四个 LayerPart 称为 layer。在生成 G-code 的过程中,都是对每个 LayerPart 单独操作,避免了打印机在两个 LayerPart 之间打印多余的东西。同时也减少了喷头运动的距离。为了生成 LayerPart,我们要使用到 Clipper 库。
  • 由于此步骤产生的数据量较大,对没有再用的数据要及时清除。
  • 根据有无 raft 基座类型对模型进行 z 方向的偏移。
  • 最后输出产生 layer parts 所用时间。

slices2polygons 函数

  • 先将为空的开头几层移除-> 处理 insets(processOozeShield)-> 处理 support(generateSupportAreas) ->处理 skins(processSkins,combineSparseLayers 合并稀疏层)-> 处理 wipeTower(processWipeTower) -> 处理平台附着(processPlatformAdhesion)

sliceDataStorage 类

RetractionConfig 类

  • 回抽丝的配置类,

GcodePathConfig 类

  • 打印过程和移动过程的参数配置类
int speed;  //打印时的速度
int line_width; //打印线宽
int filament_diameter; //
int flow;   //
int layer_thickness;  //层高
double extrusion_volume_per_mm; //每毫米挤出体积量
double extrusion_per_mm;//每毫米挤出量,用来计算总的挤出材料

const char* name;  
bool spiralize;  //是否是螺旋打印
RetractionConfig* retraction_config;//回抽配置

sliceDataStorage 类

Point3 model_size, model_min, model_max;  
Polygons skirt;          //在模型底层打印的一些轮廓线
Polygons raftOutline;               //3D 模型下面先打印一个有高度的基座的轮廓
std::vector<Polygons> oozeShield;        //oozeShield per layer
std::vector<SliceMeshStorage> meshes;
RetractionConfig retraction_config;  //回抽配置
GCodePathConfig skirt_config;      //裙摆配置
GCodePathConfig support_config;  // 支撑物配置
SupportStorage support;   //支撑物
Polygons wipeTower;    
Point wipePoint;

SliceMeshStorage

  • mesh 网格模型的数据存贮。
  • SliceLayer 类
    • 包含了一层所有的打印数据。
    • SliceLayerPart 类
      • 一层上可以打印的一个闭合的区域。(又称 islands)。

SkinPart 类

  • 一个为底部/顶部 skin 准备的连通区域。up/down skin 是模型中需要完全被填充的部位,

SupportStorage

  • 支撑结构的数据存贮。

Weaver 类

  • TODO:
发表在 CuraEngine小白学习笔记| 留下评论

CuraEngine 的详细打印参数

概述

[注意]

命令行参数

–connect ip:port

  • 连接一个通信端口。

-h

  • 显示帮助。

-v

  • 使能够输出 log 信息。(模型载入时间、切片时间等等)

-p

  • 使能够输出 Progresslogingg 信息,打印时同步信息。(几乎包括了打印的每个步骤)

-j fdmprinter.json

  • 输入 json 配置文件。这里需要说明的是,fdmprinter.json 文件里面主要是不需要经常改动的、跟打印机硬件相关的参数。
  • 如果光输入这个 json 文件,会提示 Unregistered setting,这些 setting 是要通过-s 选项输入的。

-s key=value

  • 输入 key 键对应的 value 值,中间不能有空格。

-o NameOfOutputFile.gcode

  • 输出的 gcode 文件

总的输入参数

  • CuraEngine [-h] [-v] [-p] [-s <settingkey>=<value>] -o <output.gcode> model.stl
  • 输入的文件只能是 stl 或者 STL。

内部参数

指的是通过 Cura 等图形界面软件传递给 CuraEngine 的参数。[参考了多篇文章,不一一列举出处],参数涉及到的详细概念见后文。

下面的参数参考了 Cura 不同的版本,有些可能有出入。有些跟本版本的 Curaengine 无关。这里一齐都介绍了。

  • 基本参数 Basic
    • 打印质量 Quality
      • 层高(layer height):切片每一层的高度。直接影响到打印的速度
      • 外壳厚度(Shell thickness) : 指的是对于一个原本实心的 3D 模型,在 3D 打印过程中四周生成一个多厚的塑料外壳
      • 开启回抽(Enable retraction):指的是在两次打印间隔时是否将塑料丝回抽,以防止多余的塑料在间隔期挤出,产生拉丝,影响打印质量。
      • 其中的层高和外壳厚度两个选项,都与 3D 打印机的挤出头直径密切相关。外壳厚度不能低于挤出头直径的 80%,而层高不能高于挤出头直径的 80%(14.07 版本 cura)
    • 填充 Fill
      • 底/顶厚度(Bottom/Top thickness):与外壳厚度类似,推荐这个值和外壳厚度接近,并且是层厚和喷嘴直径的公倍数
      • 填充密度(Fill Density):的就是原本实心的 3D 模型,内部网格状塑料填充的密度。这个值与外观无关,越小越节省材料和打印时间,但强度也会受到一定的影响
    • 速度和温度(Speed and Temperature)
      • 打印速度(Print speed):指的是每秒挤出多少毫米的塑料丝,吐丝速度。
      • 打印温度(Printing temperature):打印材料的温度,喷头的温度。
      • 热床温度(Bed temperature):让打印出来的 PLA 能比较牢固的粘在热床上的温度。
    • 支撑(Support)
      • 支撑类型(Support type)
        • 无支撑(None)
        • 接触平台支撑(Touching buildplate):只建立于平台接触的支撑
        • 到处支撑(Everywhere):模型内部的悬空部分也会建立支撑
    • 平台附着类型(Platform adhesion type):指是否加强模型与热床之间的附着特性
      • 无(None):直接在热床上打印 3D 模型
      • 边缘型(Brim):解决翘边的问题,这样会在第一层的周围打印一圈“帽檐”,让 3D 模型与热床之间粘的更好,打印完成时去除也相对容易。
      • 基座型(Raft):会在 3D 模型下面先打印一个有高度的基座,可以保证牢固的粘在热床上,但也不太容易去除
    • 耗材(Filament)
      • 直径(Diameter):线材的直径
      • (Flow):设置挤出塑料量相对于缺省值的百分比
  • 高级 Advanced
    • 机器相关设置(Machine)
      • 挤出头尺寸(Nozzle size):打印机喷头的直径。
    • 回抽(Retraction):
      • 回抽的速度(Speed)
      • 距离 (Distance)
    • 质量(Quality)
      • 首层厚度(Initial layer thickness):把它单独出来,其实是因为层高在一些特殊情况下可以被设置为非常小的值,但如果第一层也是这样的话就没法和热床很好的粘合了。因为这样的原因,首层厚度可以被单独指定
      • 首层线宽(Initial layer line width):也与 3D 打印对象和热床之间的粘合相关,一些情况下,可以指定一个大于 100%的值,加强这个粘合的强度。
      • 剪平对象底部(或称底层切除)(Cut off object bottom):用于一些不规则形状的 3D 对象,如果对象的底部与热床的连接点太少,也会造成无法粘合的情况,这时将这个值设置为一个大于 0 的值,3D 对象将被从底部剪平,自然可以更好的粘在热床上
      • 双击出头重叠(Dual extrusion overlap):只对双头的 3D 打印机有效,两个挤出头的挤出内容,如果有一点重叠,会得到更好的效果。
    • 速度(Speed)
      • 移动速度(Travel speed):非打印时的移动速度
      • 打印速度(print speed):打印时候喷头的移动速度。
      • 最底层打印速度(Bottom layer speed):打印第一层的速度,较低的速度可以保证牢牢黏在平台上
      • 填充速度 (Infill speed):加快打印速度可以提升时间,但会影响效果。
      • 外壳打印速度 (Outer shell speed):较低的速度打印外壳会提高打印质量,但与内部打印有巨大的差别是会影响打印质量。
      • 内壳打印速度 (Inter shell speed)
    • 制冷(Cool)
      • 打印一层最小冷却时间(Minimal layer time):为了让每一层打印完成后有足够的时间冷却。如果路径短,则会降低打印速度来满足。
      • 是否开启风扇(Enable cooling fan)
  • 专家设置
    • Infill
      • 顶部实心填充:打一个坚实的顶部表面,如不勾选,则将按照设置比例打印。
      • 底部实心填充:打一个坚实底部表面,如不勾选,则将按照设置填充比例打印。
      • 填充交叉:参数越高,表面与填充物连接坚固性越好。
    • 魔幻(Blackmagic)
      • 外部轮廓启用 spiralize:勾中后会稳固增加 z 方向,打印出一个结实底部的 单面墙.
      • 只打印模型表面 :勾中后不打因任何其他地方如填充,底部顶部等.
    • Blackmagic(另一版本的):给出两种特殊的打印方式
      • 螺旋打印(spiralize the outer contour):以螺旋线上升的线条打印模型外表面,包括底面。
      • 侧面打印(only follow mesh surface):仅仅打印模型的单层侧面,并不打印底面和顶面。
    • Brim
      • Brim 打印数量:参数越大是你打印的物体更容以粘在平台上,但同时会缩小可用打印区域
    • 底垫(Raft)
      • 额外幅度(extra margin):增大这个数字使得底座更有力,但会缩小打印区域
      • 线条间距(line spacing):底座边缘距离中心线的的距离,修改 Raft 的首层井字格接触打印平板的密度
      • 底座厚度(base thickness):最底层的厚度,厚度不宜太大,否则后期难取下来
      • 底层线宽(base line width):底层线条的宽度.
      • 接口层厚度(interface thickness):底座上层的厚度.
      • 接口层线宽(interface line width):底座接口层线条的宽度.
      • 悬空间隙:底座和表层的间隙,选用默认值易剥离
      • 表层:底座上打印表层的数量,这些曾是完全填充的.
    • 底垫(raft)另一版本
      • 留白(extra margin):控制底垫的大小的参数,底垫的形状和模型底层的形状类似,只是比底层大。底垫边线的底层边线的距离就是留白的大小。
      • 线距(line spacing): 指打印底垫时,线条之间的距离,可以控制底垫的疏密程度。
      • 基础层(base layer): 底垫下两层是基础层和接口层。基础层线宽(base line width)一般比较大。基础层厚(base thickness)也稍厚点。
      • 接口层(interface layer):
      • 空气沟(airgap):控制底垫上面和模型底面的间隙。在这个间隙中不打印任何填充物,有利于模型和底垫的分离。
      • 表面层(surface layers):存在与空气沟和接口层之间的实心层,这些层都是实心填充。
    • 抽丝(Retraction)
      • 最小空驶长度(minimum travel):需要回抽的最小空驶长度。如果一段空驶长度小于这个长度,那么便不会回抽而直接移动。
      • 是否梳理(Enable combing):让打印机在空驶前梳理一下,放置表面出现小洞。
      • 回抽最小挤出长度(minimum extrusion before retrating):防止回抽前挤出距离过小而导致一段丝在挤出机中反复摩擦而变细。如果空驶前挤出距离小于该长度,那么便不会回抽。
      • 回抽时 Z 抬升(Z hop when retrating):打印机喷头在回抽前抬升一段距离,这样可以防止喷头在空驶过程中碰到模型。当机器默认回缩的时候,会将喷头向上抬起然后再移动到下一个打印点并且降到原始高度打印.
    • 裙摆 (Skirt) :在模型底层周围打印一些轮廓线,当使用了 Brim 或 Raft 时裙摆无效。
      • 线数目(line count):裙摆线的圈数。
      • 初始距离(starting distance):最内圈裙摆线和模型底层轮廓的距离。
      • 最小长度(minimal length) : 限制裙摆线的最小长度,
    • 冷却(cooling)
      • 风扇全速高度(fan full on height):指定在某个高度,冷却风扇全速打开。
      • 最小速度(fan speed min):为了调整风扇速度去配合降低打印速度冷却。打印机为了冷却而降低速度可以达到的速度下限。\ 如果没有选择冷却头提升(cool head lift),那么即使该层打印时间大于最小层打印时间也无所谓。如果勾选了冷却头提升,那么打印机喷头会移动到旁边等待一会,直到消耗到层最小打印时间,然后回来打印。
      • 最大速度(fan speed max):
    • 填充(Infill):对底层和顶层进行特殊处理
      • 是否顶层实心填充(solid infill top)
      • 底部实心填充 (solid infill bottom)
      • 填充重叠层(infill overlap):表面填充和外壁有多大程度的重叠,值太小就会导致外壁和内部填充结合不太紧密。充时占据外框的比率,因为有可能材料挤压的程度不够,与外框粘连结合的强度不够,就可以设置这个比率,这样模型外围就不会有缝隙
      • 填充选项(infill pattern):圆形填充、直线填充、蜂窝八角形填充、矩形填充
    • 支撑 (support):设置支撑结构形状与模型的结合方式。
      • 结构类型(structure type):支撑结构的形状,有格子状(Grid)和线状(line)。
      • 填充量(fill amount): 支撑结构的填充密度。越大支撑越结实,同时也更加难于剥离。
      • X/Y 距离(X/Y distance):支撑材料在水平方向的距离。为防止支撑和模型粘到一起而设置。
      • Z 距离(Z distance):同上,太小会使得模型和支撑粘的太紧,难以剥离,太大了会造成支撑效果不好。
      • 生成支撑的悬空角度
    • 模型修复(fix horrible)
      • 类型 A http://www.docin.com/p-970237300.html%EF%BC%9A 软件试图保留子模型重叠部分的内孔和细节连接完好无损
      • 类型 B:只保留重叠部分的外型,内部细节不做处理
      • 保持开放面(keep open faces):保持模型中的小洞(自动修复这些洞)
      • 广泛拼接(extensive stitching):使用 touching polygon 算法去补模型中所有的洞,运算量大。
  • 机器设置(Machine Setting)
    • 打印宽度(Maximun width)
    • 打印深度 (Maxlumum depth)
    • 打印高度 (Maximum height)
    • Stepsper E :系到打印机挤出机构供给材料的多少,它的设置与打印速度需要配合,如果有需要的话,在高速打印的时候,需要供给材料的速度跟上,也就需要降低这个 E 值;如果打印速度太慢,供给的材料太多同样很多有可能造成打印的模型外表面变形,如果发生这种类事情,就需要增加这个 E 值
    • 喷头数量 (Extruder count)
    • 是否有打印热床(Heated bed)
    • 机器中心 (machine center 0,0) :对于一般方形打印机,应该是打印机尺寸的一半,要勾选,圆形打印机则不用。
    • 平台形状 (Build Flavor)
    • Gcode 类型 (GCode Flavor)
  • 喷头设置(print head size)
    • 用于排队打印。指将平台上的多个模型逐一打印,而不是一起打印。这样的好处是,如果打印失败,总能保证一些模型打印成功,否则,所有模型都打印失败。\ 但并不是对所有的多模型组合都能进行排队打印,比如有些模型较大,那么在排队打印的过程中可能会碰到该模型。
    • “喷头” 指的是喷头俯视图的包围矩形,“喷嘴”指的是喷嘴的位置。以喷嘴为中心点,计算喷头的 X 方向和 Y 方向的四个距离,\ 假如两个模型的左右间隙小于“size towards x_min”那么就无法从左到右排队打印。gantry height 是喷嘴下端离喷头支撑光轴在竖直方向的距离。

    pentou.png

  • 通信设置(Communication settings)
    • 通信串口号(Serial port)
    • 波特率(Baudrate)

详细概念

  • 以下内容完全照抄 http://www.nanjixiong.com/thread-51870-1-1.html 这篇介绍很专业,大家都应该看看。
  • Cura 的架构
    • Cura 是一个 python 语言实现,使用 wxpython 图形界面框架的 3D 打印切片界面软件,说它是界面软件是因为 Cura 本身并不会进行实际的切片操作。实际的切片工作是由另外一个 C++语言实现的 CuraEngine 命令行软件来具体负责的,用户在 Cura 提供的界面上的绝大多数操作如加载模型、平稳旋转缩放、参数设置等最终会转换成并执行一条 CuraEngine 命令;CuraEngine 把输入的 STL、DAE 或 OBJ 模型文件切片输出成 gcode 字符串返回给 Cura;Cura 再把 gcode 在 3D 界面上可视化成路径展现给用户。
    • 我所参考的版本是 15.04,15.06 之后 Cura 和 CuraEngine 都有较大的改动,但核心思想没变。所以本文分析的代码也到 15.04 为止。
    • 言归正传,下面我们将开始一步一步揭开 CuraEngine 把一个模型文件转换成为 gcode 的过程。
  • 切片流程概述

    从总体上讲,CuraEngine 的切片分为五个步骤: dayinkuangjia.png

    • 步骤一:模型载入
      • 有一点 3D 编程经验的人都知道,计算机中的 3D 模型大多是以三角形面组合成的表面所包裹的空间来表示的。三角形作为 3D 模型的基本单元,有结构简单,通用性强,可组合成任意面的特点;空间坐标中只要三个点就可以表示一个唯一的三角形,两点只能表示一条直线,而再多的直线也无法组成一个平面;空间中的任意三个不共线的点都可以组成一个三角形,而四个点所组成的四边形就必需要求四点共面;任意的表面都可以拆解成三角形,一个四边形可以拆解成两个三角形,但一个三角形确没有办法用四边形组合而成。计算机所擅长的事情就是把简单的事情不断重复,而三角形正是因为这些特性,成为了计算机 3D 世界的基石。
      • CuraEngine 内部也是用三角形组合来表示模型的,不过同样一个三角形组合,确有无穷多种数据结构来进行存储,CuraEngine 切片的第一步,就是从外部读入模型数据,转换成以 CuraEngine 内部的数据结构所表示的三角形组合。
      • 有了三角形组合还不够,CuraEngine 在载入模型阶段还要对三角形进行关联。两个三角形共有一条边的,就可以判断它们为相邻三角形,一个三角形有三条边,所以最多可以有三个相邻三角形,一般而言,如果模型是封闭的,那它的每一个三角形都会有三个相邻三角形。
      • 有了三角形的相邻关系,就可以大幅提高下一个步骤分层过程的处理速度。Cura 之所以成为当前市场切片速度最快的软件,这是其中最显著的优化之一。
      • 模型载入更详细的过程会另文分析,敬请期待。
    • 步骤二:分层
      • 如果把模型放在 XY 平面上,Z 轴对应的就是模型高度。我们把 XY 平面抬高一定高度再与模型的表面相交,就可以得到模型在这个高度上层切片。所谓的分层就是每隔一定高度就用一个 XY 平面去和模型相交作层切片,全部切完后就可以得到模型在每一个高度上的轮廓线。就像是切土豆片一样,把一个圆的或不圆异或不管什么奇形怪状的土豆用菜刀一刀一刀切开,最后就能得到一盘薄如纸片的土豆片,当然那还得你的刀功要足够好才行。
      • 分层本质上就是一个把 3D 模型转化为一系列 2D 平面的过程,自此之后的所有操作就都是在 2D 图形的基础上进行了。
      • 在前面模型载入阶段我说到了 CuraEngine 埋了一个三角形关联的伏笔,作用是什么,现在就可以揭晓了。我们知道,两个平面相交,得到的是一条直线,一个平面和一个三角形相交,就得到一条线段。当然也有可能什么也得不到,平台平行啦,三角形的三个点都在平面的同一面之类,这些我们可以不管,我们现在只关心和平面有交集的那些三角形即可。我们把一个平面和所有的三角形都相交了一遍,得到了许许多多的线段,但我们需要的是 2D 图形,三角形是 2D 图形,四边形,任意多边形都是 2D 图形,但线段不是。所以我们就要把这些线段试着连成一个多边形,那么问题来了,要把这些线段连起来,只能两个两个地去试,看看它们是不是共端点了,粗算一下,每一层都是平方级的复杂度,再算上层数,那就是三次方级了。但现在,我们知道了三角形的关联关系,两个关联的三角形,如果都与一个平面相交,那它们的交线一定也是关联的,这一下,每一条线段只需要判断三它与它相邻三角形,看看与这个平面有没有交线即可,一下子就把问题的复杂度降了一个次元。速度自然可以有质的提升。
      • 分层更详细的过程会另文分析,敬请期待。
    • 步骤三:划分组件
      • 经过分层之后,我们得到了一叠 2D 平面图形。接下来需要做的事情就是对每一层的平面图形进行跑马圈地,标记出哪里是外墻、内墙、填充、上下表面、支撑等等。
      • 3D 打印在每一层是以组件单位,所谓组件指的就是每一层 2D 平面图形里可以连通的区域,比如左图就可以拆分为黄绿蓝三个组件。而打印的顺序就每打印完一个组件,接着会挑选一个离上一个组件最近的组件作为下一个进行打印,如此循环直至一层的组件全部打印完成;接着会 Z 轴上升,重复上述步骤打印下一层的所有组件。
      • 至于每一个组件怎么打印,就和我们手工画画一样,先打边线再对边线内部填充。边线可以打印多层,最外层的边线称为外墙,其它的统称为内墙,CuraEngine 之所以要对内外墙进行区分,是为了可以为它们定制不同的打印参数:外墙会被人观察到,所以可以采用低速以提高表面质量,内墙只是起增加强度的作用,可以稍稍加快打印速度以节省时间。这些都可以在 Cura 界面的高级选项里进行配置。
      • 有一点值得注意的是,也是我半年打印的经验,由于 FDM 挤出装置的特性所至,挤出机的挤出通过影响的只是加热腔里的熔丝压力,间接决定了喷头的挤出速度,而加热腔本身对于压力就有一个缓冲作用,所以挤出速度的突变并不会使得喷头的挤出速度立即跟着变化,而是有一个延迟,这一点在远端送丝的机器上更为明显。而恰恰我们公司的主打产品 F3CL 就是远端送丝,在 Pango 中考虑到这个问题,并加上了特殊处理,事实证明的确对打印质量有一定的提升。具体办法是什么,我先卖个关子,会 Pango 的专文里进行讲解。
      • 内外墙标记完之后就是填充和上下表面的标记了,填充有一个填充率,0%填充率就是无填充,100%就是打成一个密实的平面,所以上下表面就是填充率为 100%的填充。中间的填充率自然介于两者之间,就像一张渔网,填充率越高网眼越细。
      • 软件会先把内墙里面的部分统统标记成填充,之后再进一步判断其中有哪些部分要转换成为上下表面。至于是哪些部分,在设置里会有一个上下表面层数的设置,它代表了模型的上下与空气接触的表面有几层,这里就会用到这个参数,CuraEngine 会把当前层上下 n 层(上下表面层数)取出来与当前层进行比较,凡是当前层有而上下 n 层没有的部分就会被划归到表皮。而原来的填充区域在割除被划到表皮的部分后剩下的部分就是最终的填充区域。
      • CuraEngine 在处理过程中大量用到了 2D 图形运算操作,有关 2D 图形的运算,有很多人研究,也被做成许多成熟的库可以调用。CuraEngine 的作者拿来主义,选取了一个他认为比较好用的库,叫 ClipperLib 的库直接内嵌到软件之中,ClipperLib 所使用的 2D 图形算法也很著名,叫 Vatti’s clipping algorithm,很复杂,我也没有完全搞懂,有兴趣的读者要是搞懂了可以多多交流。ClipperLib 的网址是:http://www.angusj.com/delphi/clipper.php
      • 这里我先简单介绍一下 CuraEngine 所用到的几种 2D 图形的运算,都是由 ClipperLib 实现的:交、并、差、偏移。与集合操作类似先看图:

      tuxingyunsuan.png

      • 图形相交 二元图形操作,最终结果为两个图形共同包含的区域。记作:A * B
      • 图形相并 二元图形操作,最终结果为两个图形其中的一个或两者所包含的区域。记作:A + B
      • 图形相减 二元图形操作,最终结果为属于前者但不属于后者的区域。记作:A – B
      • 图形偏移(外扩) 一元图形操作,最终结果为图形区域的边界向外扩展指定的距离。
      • 图形偏移(内缩)

        • 一元图形操作,最终结果为图形区域的边界向内收缩指定的距离。内缩与外扩互为逆运算。
        • 这些就是 CuraEngine 所用到的 2D 图形操作,运算不多,确可以做许许多多的事情,比如上面所说的上下表面计算,就可以用数学公式来表示:
          • 表面(i) = [填充(i) – 层(i + n)] + [填充(i) – 层(i – n)]
          • 填充(i) = 填充(i) – 表面(i)
        • 其中,i 为当前层号,n 为上下表面层数(可以不一样)。多简单,数学就是这么任性!
        • 同样的,组件里面内外墙,填充怎么划分,只用一个内缩运算就可以搞定:
          • 外墙 = 组件.offset(-线宽)
          • 内墙 1 = 组件.offset(-线宽 * 2)
          • 内墙 n = 组件.offset(-线宽 * (n + 1))
          • 填充 = 组件.offset(-线宽 * (n + 2))
        • 如果模型无需支撑,那组件划分到这里就可以收工。否则,接下就是计算支撑的时间了。
        • 我用 CuraEngine 半年下来觉得它最大的不足就是在支撑上,这也是我在 Pango 投入最大精力要改进的地方,这里就先简单介绍一下 CuraEngine 所用的支撑算法。
        • CuraEngine 首先把整个打印空间在 XY 平台上划分成为 50um*50um 的网格,每个网格的中心点再延 Z 轴向上作一条直线,这条直线可能会与组成 3D 模型的三角形相交,三角形与直线的交点以及这个三角形的倾斜度会被记录到网格里面。
        • 现在每个网格里会记录下一串被称为支撑点的列表,每个支撑点包含一个高度和一个倾斜度信息。接下来会对每个网格的支撑点列表按照高度从低到高排序。根据这些信息就可以判断模型上任意一个点是否需要支撑了,怎么判断,我们看图说话:

        zhicheng.png

        • 让我们从底面开始延着一条网格中心往上走,起始我们是在模型外部,当遇到第一个支撑点的时候,就从模型外部进行了模型内部,我们称这个支撑点为进点。
        • 继续向上,遇到了第二个支撑点,从模型内部又退到了模型外部,我们称这个支撑点为出点。
        • 接着向上,我们可以发现,进点与出点总是交替出现的。
        • 利用这个规律,对于模型上任何一个点,我们只要找到这个点所对应的网格,再找到这个网格里在这个点以上最近的一个支撑点,我们就可以得到两个信息:这个点之上是否有模型悬空;这个点上面的悬空点的面的倾斜度是多少。
        • Cura 界面的专家设置里面有支撑角度的设置,如果一个点处于模型悬空部分以下,并且悬空点倾斜度大于支撑角度,那这个点就是需要支撑的。所一个平台上所有的需要支撑的点连接起来围成的 2D 图形就是支撑区域。 zhichengjiaodu.png
        • CuraEngine 所使用的支撑算法比较粗糙,但胜在速度很快。先不说网格化后失去了精度,通过倾斜角度来判断,模型下方一旦倾斜角发生了突变,像左图这种从负 45 度一下突变成正 45 度,倾斜角判断无能为力,除非把它改大到 60 度,这样的话,整个模型都会被过度支撑。这样矫枉过正,既不科学,也浪费材料和打印时间,还会对模型表面质量带来不好的影响。
        • 科学的支撑算法应该是找到模型局部最低点进行支撑,最低点以上不一定需要支撑。因为 FDM 材料本身的粘性,使得材料的走线可以有一部分悬空而不坍塌,这个效果被称为 Overhang,只要上层材料的悬空距离小于一定的值,它就不需要支撑,这个距离以我的经验应该在 1/4 到 1/2 线宽之间。 overhang.png
        • 支撑范围确定之后,也和组件一样,可以有外墙、内墙、填充、表面。依样画葫芦即可。CuraEngine 对于支撑,只会生成外墙和填充。
        • 组件和支撑就是 CuraEngine 在这一步所生成的结果,这一步可以说是整个切片过程的核心,更详细的过程会另文分析,敬请期待。
    • 路径生成

      lujingshengcheng.png

      • 地圈好了,就该在里面种菜了。这一步路径生成就要开始规划喷头在不同的组件中怎么运动。路径按大类来分,有轮廓和填充两种。
      • 轮廓很简单,沿着 2D 图形的边线走一圈即可。前一步所生成的外墙、内墙都属于轮廓,可以直接把它们的图形以设置里的线宽转换为轮廓路径。
      • 填充稍微要复杂一些,2D 图形指定的只是填充的边界,而生成的路径则是在边界的范围内的条纹或网格结构,就像窗帘或者渔网,如左图。这两种就最基本的结构,当然也许你还可以想出其它花式的填充,比如蜂窝状或者 S 型,这些在新的 Cura 或者别的什么切片软件里可能会实现,但我打印下来还是这两种基本结构更让人放心。
      • CuraEngine 在专家设置里可以对填充类型进行选择,里面除了条纹和网格外还有一个自动选项,默认就是自动。自动模式会根据当前的填充率进行切换,当填充率小于 20%就用条纹填充,否则使用网格填充。因为网格结构虽然更为合理,但它有一个问题,就是交点的地方会打两次。填充率越高,交点越密,对打印质量的影响会越大。我们知道,表面就是 100%的填充,如果表面用网格打,不但无法打密实,表面还会坑坑洼洼,所以 100%填充只能用条纹打,这就是 CuraEngine 推荐自动模式的原因。
      • 至于填充率,就反映在线与线的间距上。100%填充率间距为 0;0%填充率间距无限大,一根线条也不会有。
      • 每个组件独立的路径生成好了,还要确定打印的先后顺序。顺序先好了可以少走弯路,打印速度和质量都会有提升。路径的顺序以先近后远为基本原则:每打印完一条路径,当前位置是上一条路径的终点;在当前层里剩下还没打印的路径中挑选一条起点离当前位置最近的一条路径开打。路径的起点可以是路径中的任意一个点,程序会自行判断。而路径的终点有两种可能:对于直线,图形只有两个点,终点就是除起点之外的那个点;对于轮廓,终点就是起点,因为轮廓是一个封闭图形,从它的起点开始沿任意方向走一圈,最后还会回到起点。CuraEngine 对路径选择做了一个估值,除了考虑到先近后远外,还顺便参考了下一个点相对于当前点的方向,它的物理意义就是减少喷头转弯。赛车在直道上开得一定比弯道快,不是么。
      • 路径的顺序也确定了,还有一个问题需要考虑:如果前后两条路径首尾相连,那直接走就是了,但大多数情况并非如此,前一条路径的终点往往和后一条路径起点之间有一段距离。这时候去往下一点的路上要小心了,肯定不能继续挤出材料,否则轻则拉丝,重则模型面目全非。这段路喷头就需要空走,即喷头只移动,不吐丝,那只要把挤出机停下来不转就行了吗?也不行,因为前面分析过,挤出机的速度要传导到喷嘴,有一个延迟,不是你说停它就立即停下来的。这是 FDM 打印的通病,解决办法就是回抽。所谓回抽,就是在空走之前先让挤出机高速反转一段材料,这样就可以瞬间把加热腔里的材料抽光,再移动过去,中间就不会挤出材料,到了下一个点,在打印之前,先把刚才抽回去的丝再按一样的长度放回来,继续打印。回抽可以很好地解决空走拉丝的问题,但是它很慢,以抽一次 0.5 秒来算的话,如果打印一个表面,0.4 线宽,10 厘米的距离至少回抽 25 下,10 几秒钟的时间一层,几百上千层打下来,光回抽所用的时间就是几个小时,是可忍孰不可忍! huichou.png
      • CuraEngine 给我们提供了解决方案就是 Comb,也就是绕路。我们先来看,是不是所有的回抽都是必需的呢?不回抽会拉丝是肯定的,但如果需要空走的路径本来就要打印的,那拉丝又有何妨。按这个思路,就可以给每个组件设定一个边界,只要路径的起点和终点都在这个边界之内的,空走都不回抽。这样可以解决 80%的问题,但如果是左图这样的情况就行不通。
      • 红色是起点,绿色是终点,直接走过去会走出边界的范围。这时我们就要绕一点路,走一条曲线到达我们的目的地。这就是 Comb 所做的事情,在 Cura 专家设置里面可以对 Comb 进行设置,选择开启、关闭还有表面不 Comb。Comb 可以大幅节省打印时间,但是同一个地方打印多次对模型质量还是会有细微的影响,个中利弊,交给用户自己判断。
      • Comb 的调整是个细致活,Pango 花了相当多的时间来微调 Comb 功能以求达到更好的效果,过程繁琐,不再赘述。
      • 至此路径生成完成,更详细的过程另文分析,敬请期待。
    • gcode 生成
      • 路径都生成好了,还需要翻译对打印机可以实别的 gcode 代码才行。这一步花样不多,按部就班即可。
      • 先让打印机做一些准备工作:归零、加热喷头和平台、抬高喷头、挤一小段丝、风扇设置。
      • 从下到上一层一层打印,每层打印之前先用 G0 抬高 Z 坐标到相应位置。
      • 按照路径,每个点生成一条 gcode。其中空走 G0;边挤边走用 G1,Cura 的设置里有丝材的直径、线宽,可以算出走这些距离需要挤出多少材料;G0 和 G1 的速度也都在设置里可以调整。
      • 若需回抽,用 G1 生成一条 E 轴倒退的代码。在下一条 G1 执行之前,再用 G1 生成一条相应的 E 轴前进的代码。
      • 所有层都打完后让打印机做一些收尾工作:关闭加热、XY 归零、电机释放。
      • 生成 gcode 的过程中,CuraEngine 也会模拟一遍打印过程,用来计算出打印所需要的时间和材料长度,这些也会写在 gcode 的注释里供用户参考。
      • gcode 生成不用另文详细分析,但是 gcode 的说明还是可以专文分析一下,敬请期待。

对应的源码

配置的导入

可以通过 json 文件导入注册配置,通过命令行-s 参数导入非注册配置(非注册配置会再输出流中显示 warning:setting an unregistered setting)

  • 导入 json 配置
    1. settingCategory 类
      • 该类对应了 fdmprinter.json 文件的层次结构。用于设置类别。
    2. SettingConfig 类
      • settingCategory 下一级目录的配置结构。
    3. SettingRegistry 类
      • 这个类包含了所有已知的注册键值。也包含了从 json 文件中获得的目录层次结构。是要用到的类。

        static SettingRegistry* getInstance();  //返回该类的静态实例,里面有以后要用的键值对和目录层次表
        bool settingExists(std::string key) const; //查看 key 是否已经被设置
        const SettingConfig* getSettingConfig(std::string key);   //或得 key 对应的 settingConfig 对象
        bool loadJSON(std::string filename);  //导入 json 文件!
        
      • 如果 json 文件解析有误,会报错:offset 偏移量 :错误原因。
    4. fdmprinter.json 文件
      • 这个文件是默认文件,如果命令行参数没有指定 json 文件,就导入这个。
      • 来看看里面的内容(可以通过 JSON Viewer 来查看 json 文件)。首先,json 文件里面包含了三个大类(fdmprinter.json 在群文件里有)
        • visible
        • machine_settings
        • categories
      • 事实上,根据源码,应该包含以下:
        • machine_settings
        • mesh_settings //这里包含了 mesh_position_x ,mesh_postion_y, mesh_postion_z
        • categories
          • 循环递归导入 label 对应的值、对应的 settings 配置。
          • 一个 category 项包括名字 name、label 和对应的描述值、settings 子 category。
            • 对于一个 settings,可以包含多个子 category,直到根节点。
            • 子 category 和根节点包含了 type、default、unit 等信息。
    5. SettingBase 类
      • 主要包含一个键值对表。对 SettingRegistry 的数据进行检查并且存入。

        bool hasSetting(std::string key);  //是否存在该键
        void setSetting(std::string key, std::string value);//设置键值
        std::string getSettingString(std::string key); //返回键对应值
        
        EGCodeFlavor getSettingAsGCodeFlavor(std::string key); //使用的 gcode 类型
        EFillMethod getSettingAsFillMethod(std::string key);  //内部填充方法类型
        EPlatformAdhesion getSettingAsPlatformAdhesion(std::string key); //平台附着类型
        ESupportType getSettingAsSupportType(std::string key);  //支撑类型
        
      • 需要注意的是,setSetting 方法会对 key 进行检查,如果之前不存在 key(即不是从 json 文件中导入的),会报 warning:setting an unregistered setting. 其中-s 参数对应的命令行配置都是通过这个方法导入的,因此-s 报警告是正常现象。
      • getSettingString 方法如果没有找到 key 对应的值,会把 key 对应的值置空。并且报错 Unregistered setting。

fdmprinter.json 和 PrintConfig.josn 文件里的参数

layer_height

  • layer_height_0
    • bottom layer 最底层的层厚度。厚的话使与底层结合牢固。
  • layer_height
    • 每层的厚度,质量一般时取 0.1mm
  • line_width
    • wall_line_width
      • 一个 shell 的线宽。
      • wall_line_width_0: shell 最外层的线宽
      • wall_line_width_x: shell 除了最外壳的其他线宽
    • skirt_line_width
    • skin_line_width
    • infill_line_width
    • support_line_width
    • support_roof_line_width
    • prime_tower_line_width

meshfix

  • 在 printconfig.json 文件里有,fdmprinter.json 文件里则没有。表示各种模型修复方式,在 Slicer 类中被用到。
  • meshfix_union_all
  • meshfix_union_all_remove_holes
  • meshfix_extensive_stitching
  • meshfix_keep_open_polygons

platform_adhesion

  • adhesion_type
    • 附着类型,有 none、brim、raft 三种。
  • skirt_gap
    • skirt 和打印的第一层的水平距离。当为 none 时才起作用。
  • skirt_minimal_length
    • skirt 的最小长度。
  • skirt_line_count
    • skirt 是在打印第一层周围的 line,它为喷头的初始化做准备
  • brim_line_count
    • brim lines 的使用量,越多越牢固,但是限制的打印范围。
  • raft_margin
    • 在打印物体周围额外的 raft 区域。
  • raft_base_ticknesse
    • raft layer 的层厚。为了牢固地固定在热床(bed)上这层不能太薄。
  • raft_interface_thickness
    • interface raft layer 的层厚。(底座上层的厚度)
  • raft_surface_layers
    • 在 raft layer 第二层上的 surface 层层数。这个也是需要被完全填充的。
  • raft_surface_thickness
    • surface 层厚。

blackmagic

  • magic_spiralize
    • 在 out edge 呈螺旋状运动。这会再处理过程中稳定的线性增加 z 坐标值。这最终会将一个实体变成有实心底部的外墙结构。

shell

  • top_bottom_thickness
    • top_thickness : 顶层 layer 的厚度。
    • bottom_thickness : 底层 layer 的厚度。
  • shell_thickness
    • outside shell 在水平和竖直方向上的 shell 厚度。用于决定 perimeter line 的数量和厚度,以及 top/bottom layers 的数量。
    • wall_thickness
      • outside wall 在水平方向上的厚度。
      • wall_line_count : 又称 perimeter lines。影响打印结构和强度。 代码中为 insetCount
      • wall line width
        • wall_line_width_0 : 第一个 wall 的线宽
        • wall_line_width_x : 其他 wall 的线宽
        • skirt_line_width : skirt 的线宽
        • skin_line_width : skin 的线宽,top/width 的打印线宽
        • infill_line_width : inner 内部填充线宽
        • support_line_width : 支撑结构线宽
    • alternate_extra_perimeter :每隔两层加额外的 wall,会增强 infull 和 wall 的结核性,但是会破坏表面质量。
    • top_bottom_thickness
      • top_thickness :top layers 的厚度。
        • top_layers : top layers 的数量
      • bottom_thickness :bottom layers 的厚度。
        • bottom_layers : bottom layers 的数量
  • wall_overlap_avoid_enabled
    • 移除重叠的 parts,不造成重叠挤出。
  • skin_outline_count
    • 在 skin 区域周围的 line 数量。
  • fill_perimeter_gaps
    • 填充 wall 之间的间隙,有 Nowhere、everywhere、skin 三种类型。
  • xy_offset
    • 每层所有多边形的偏移,并不是空间位置的偏移,而是为了修复孔洞,正值可以补偿大的孔洞,负值可以补偿微小的孔洞。

infill

  • fill_sparse_thickness
    • fill_sparse_combine : 用来进行稀疏填充的被合并的 layers 的数量。
  • fill_sparse_density
    • fill_pattern
      • 手动选择 grid 填充或者 line 填充。
    • infill_line_distance
      • 填充线之间的距离。
  • fill_overlap
    • 允许 infill 和 wall 的重叠量。稍微一点的话可以增强结合性。

shield

  • ooze_shield_enabled
    • 使能外部渗漏保护层。它会在 object 外面打印一层 shell。
  • ooze_shield_angle
    • 渗漏保护层的最大角度。0 表示竖直,90 表示水平。小的角度保证了渗漏保护层安全性,但是需要更多的材料。
  • ooze_shield_dist
  • draft_shield_enabled
  • draft_shield_dist
  • draft_shield_height_limitation
  • draft_shield_height
发表在 CuraEngine小白学习笔记| 23条评论

切片算法(1)——底层数据结构

概述

  • 终于可以开始将切片算法了。 图切片算法.png
    • 这里红色标出的是已经讲过的类,不要看那么多的类,其实还有 clipper 没有标出来呢!clipper 是专门处理封闭曲线的库,并不是 CuraEngine 项目的作者开发的。
    • 先看看底层的一些类,基本不涉及 Clipper 库的。

切片流程

  • 插一段切片流程描述。详见切片流程 以下引用,原作者勿怪。。
    • 在知道 CuraEngine 的工作原理之前,需要先知道 STL 格式的含义。.stl 文件是在计算机图形应用系统中,用于表示三角形网格的一种文件格式。它的文件格式非常简单,应用很广泛。STL 是最多快速原型系统所应用的标准文件类型。STL 是用三角网格来表现 3D CAD 模型。Stl 文件中存放的是无数个空间三角面的位置信息(空间中,每个三角面的确定是通过它三个定点的坐标来的)。所以,我们如果要对一个 3d 模型进行切割,实际上就是用一个平面来和无数小三角形做相交,得到很多条交线,我们把这些交线连接起来就得到了切平面。
    • 总流程

      • The Cura Engine is structured as mainly .hfiles. This is not standard for an C++ project. However, using less cpp filesmakes the optimizer work harder and removes linking error issues. It’s partialya result of lazyness but also for optimalizations.

      The .h files contain different steps calledfrom the main.cpp file. The main.cpp file contains the global slicing logic.
      The slicing process follows the followingglobal steps:

      • Load 3D model
      • Analize and fix 3D model
      • Slice 3D model into 2D layers
      • Build LayerParts from sliced layers
      • Generate Insets
      • Generate up/down skins areas
      • Generate sparse infill areas
      • Generate GCode for each layer
      • Each step has more logic in it. But this isa general overview. All data for the engine is stored in the”SliceDataStorage”. It’s important to remember that only the data fromthe previous step is valid.

      Coordinates are stored in 64bit integers asmicrons in the code. So if you see a value of 1000 then this mean 1mm ofdistance. This is because Clipper works on 64bit integers and microns give ahigh enough resolution without limiting the size too much. Note that there aresome bits and pieces of code that need to be careful about 64bit overflows,especially calculating lengths sqrt(x*x+y*y) can cause overflows.

      • 首先,Cura 不是一个标准的 C++工程,他大部分的函数都是在.h 文件中实现的,这样做,使得在编译过程中优化出现了很多的错误,这主要是由于懒的原因….(..请跳过)。
      • 切片程序的主要过程如下:
  • 导入 3D 模型(STL,OBJ 等等)。
  • 分析并修复 3D 模型(源码里面貌似木有这一步…)。
  • 将 3D 模型切割成 2D 层。
  • 用上一步得到的 2D 图层形成 LayerParts(他们自己的叫法),因为一层里面,很有可能有很多个不同的多边形,比如桌子,他的四个角,切出来后是四个圆形,上一步中只是得到了四个圆形,而没有确定这四个圆形是属于同一层的。
  • 进一步确定 LayerParts 中,各个 part 间的关系,比如得到了两个圆,大圆套小圆,我们就需要确认,小圆是空心的,而大圆和小圆形成的圆环是实心的。
  • 将需要实心打印的部分标记出来(100%填充)。
  • 将需要空心打印的地方打印出来(部分填充)。
  • 根据生成的 LayerParts 生成每一层的 G-code。

    • 上述的每一步都有更多的逻辑关系在里面,但这只是一个工作的大概流程。切割引擎所有的数据都存放在一个叫 SliceDataStorage 的类里面。记住,上述的每一步都是基于前一步的数据来进行的。这里严格按照上述的流程来处理 3D 模型生成 G-code。另外,在代码里面,坐标是用 64 位整数的形式存在的,比如,你在代码中看到的 1000,他实际代表了 1mm。这样做是因为 Clipper 使用了 64 为整数来表示距离。
    • 源码中的几个类
      1. OptimizedModel
    • OptimizedModel 也是一个 3D 模型,只是他是对一开始导入的模型进行的优化,去除了 3D 模型中多余的数据,同时确立了 3D 模型中每个三角面之间的拓扑关系。这是整个软件最为核心的一部分之一。他为后面一步进行切割做好了准备,没有他 slice 无法进行。
      1. Slicer
    • 我们通常都把由 3D 模型生成 G-code 的过程叫做 slicing.在 CuraEngine 中,Slicer 只是数量很小的一部分代码,用于生成 layers。每个 layer 都有闭合的 2D 多边形。这些多边形的形成有两步。
      1. 第一步,用一个切割平面同所有三角形做相交运算,得到和这个平面相交的线段就是属于这个 layer 的,这些切割后得到的线段称之为”linesegment”。此时,layer 里面只是有一些零散的线段。
      2. 第二步,将这些 linesegment 连起来,形成封闭的多边形。
    • 由于 OptimizedModel 已经将各个相邻三角形之间的关系确立好了,这里的 slice 速度变得很快。在进行完 slice 之后,我们就得到了封闭的多边形曲线,这些曲线,要交给 Clipper 库来进行下一步的处理。Clipper 库只能用于处理 2D 的封闭多边形模型。
      1. LayerParts
    • LayerParts 是需要掌握的一个重要概念。LayerParts 是一个单独的 layer 中的一部分。比如,切割一个正方体,我们只会得到一个 LayerPart,但是,如果切割一个桌子,这个桌子有四个脚,我们就会得到四个切面,也就是四个 LayerPart,我们把这在同一平面的四个 LayerPart 称为 layer。在生成 G-code 的过程中,都是对每个 LayerPart 单独操作,避免了打印机在两个 LayerPart 之间打印多余的东西。同时也减少了喷头运动的距离。为了生成 LayerPart,我们要使用到 Clipper 库。
      1. Up/Down skin
    • 这部分的功能是确定模型中,需要完全被填充的部位,这里大量使用了 Clipper 库里面的布尔运算。如果自己看 Cura 的代码会发现,这里的 skin(完全填充)和 sparse fill(稀疏填充)的代码是几乎一样的,只是设置了不同的填充比例而已。注意,这一步只是标记了需要填充的区域,真正的填充操作是在下面一步生成 G-code 的过程中进行。
      1. G-code 生成器
    • G-code 生成器有非常多的代码,这里给出他主要的几个点:
      1. PathOrderOptimizer:这部分代码是路径优化代码,计算最优路径。提供很多歌多边形,然后找到最好的顺序和最好的路径来走完他们。比如,你打印完这一个 LayerPart 后,你需要找到下一个离他最近的 LayerPart 多边形来打印
      2. Infill:这部分代码会在一个区域里生成一组线。
      3. Comb:这部分的代码,是为了避免在打印机不喷丝时移动喷头而不会同打印好的层发生接触从而产生一些不好的洞。
      4. GCodeExport:导出 G-code 分为两个步骤,首先,他将需要打印的那一层(layer,不是 LayerPart)的所有路径收集起来,包括移动,打印,挤出宽度等。然后生成最终的 G-code。只有这一部分的代码是和 G-code 直接相关的。要生成不同的 G-code,只需要在这里做少量的调整即可。另外,对模型体积的计算,也是在这一步中计算出来的。
        1. Insets
    • 有时又叫做”Perimeters”(围墙)或者 “Loops”(环圈)。处理这个 clipper 做了大部分的工作。
      • 英文

        OptimizedModel
        ============
        The OptimizedModel is a 3D model stored with vertex<->face relations. This gives touching face relations which are used later on to slice into layers faster.

        Slicer
        ====
        While usually the whole GCode generation process is called ‘slicing’, the Slicer in the CuraEngine is the piece of code that generates layers. Each layer contains closed 2D polygons. These polygons are generated in a 2 step process. First all triangles are cut into lines per layer, for each layer a “line segment” is added to that layer. Next all these line-segments are connected to eachother to make Polygons. The vertex<->face relations of the OptimizedModel help to make this process fast, as there is a huge chance that 2 connecting faces also make 2 connecting line-segments. This code also patches up small holes in the 3D model, so your model doesn’t need to be a perfect Manifold. It also deals with incorrect normals, so it can flip around line-segments to fit end-to-end. After the Slicer we have closed Polygons which can be used in Clipper, as Clipper can only opperate on closed 2D polygons.

        LayerParts
        ========
        An important concept to grasp is the idea of LayerParts. LayerParts are seperate parts inside a single layer. For example, in a solid cube each layer has a single LayerPart. However, in a table the layers which cover the legs have a LayerPart per leg, and thus there will be 4 LayerParts. A LayerPart is a seperated area inside a single layer which does not touch any other LayerParts. Most operations run on LayerParts as it reduces the amount of data to be processed. During GCode generation handling each LayerPart as a separate step makes sure you never travel between LayerParts which reduces the amount of external travel. LayerParts are generated after the Slicer step.

        In order to generate the LayerParts, Clipper is used. A Clipper union with extended results gives a list of Polygons with holes in them. Each polygon is a LayerPart, and the holes are added to this LayerPart.

        Insets
        ====
        Insets are also called “Perimeters” or “Loops” sometimes. Generating the insets is only a small bit of code, as Clipper does most of the heavy lifting.

        Up/Down skin
        ==========
        The skin code generates the fully filled areas, it does this with some heavy boolean Clipper action. The skin step uses data from different layers to get the job done. Check the code for details. The sparse infill area code is almost the same as the skin code. With the difference that it keeps the other areas and uses different offsets.

        Note that these steps generate the areas, not the actual infill lines. The infill line paths are generated later on. So the result of this step are lists of Polygons which are the areas that need to be filled.

        GCode generation
        ==============
        The GCode generation is quite a large bit of code. As a lot is going on here. Important bits here are: PathOrderOptimizer: This piece of code needs to solve a TravelingSalesmanProblem. Given a list of polygons/lines it tries to find the best order in which to print them. It currently does this by finding the closest next polygon to print. Infill: This code generates a group of lines from an area. This is the code that generates the actuall infill pattern. There is also a concentric infill function, which is currently not used. Comb: The combing code is the code that tries to avoid holes when moving the head around without printing. This code also detects when it fails. The final GCode generator uses the combing code while generating the final GCode. So they interact closely. GCodeExport: The GCode export is a 2 step process. First it collects all the paths for a layer that it needs to print, this includes all moves, prints, extrusion widths. And then it generates the final GCode. This is the only piece of code that has knowledge about GCode keywords and syntax;meshmdhfdhfdhf to generate a different flavor of GCode it will be the only piece that needs adjustment. All volumatric calculations also happen here.

数据结构

见后文

  1. WeavesegmentType 枚举

    ENUM( WeaveSegmentType, UP, DOWN, FLAT, MOVE, DOWN_AND_FLAT);
    
    • 网格分割的类型
  2. WeaveConnectionSegment 结构体

    //成员变量
    Point3 to;
    WeaveSegmentType segmentType;
    
  3. PolyLine3 结构体

    //成员变量
    Point3 from;
    std::vector<WeaveConnectionSegment> segments;
    
  4. WeaveConnectionPart 结构体

    //成员变量
    PolyLine3 connection;
    int supported_index;
    

next..

发表在 CuraEngine小白学习笔记| 11条评论

libArcus库

概述

  • libarcus库包含了c++和python的类(这里没用到python),用来创建一个socket。这个socket基于protobuf,用来发送和接收信息。 说白了就是在Cura(图形界面,前端)和CuraEngine(后端)传递信息。搞得这么麻烦。
        /*This library contains a C++ and Python3 class for creating a socket in a thread
    and using this socket to send and receive messages based on the Protocol Buffers
    library. It is designed to facilitate the communication between Cura and its
    backend and similar code.*/
    

源码

  1. 项目有以下文件:
    • Types.h
      • 声明了protobuf内置的类message,
      • 在命名空间SocketState中Socket状态的枚举。
        SocketState::enum State
        {
            Initial, ///< Created, not running.
            Connecting, ///< Connecting to an address and port.
            Connected, ///< Connected and capable of sending and receiving messages.
            Opening, ///< Opening for incoming connections.
            Listening, ///< Listening for incoming connections.
            Closing, ///< Closing down.
            Closed, ///< Closed, not running.
            Error ///< An error happened.
        };
        
    • SocketListener.h
      • 定义了类SocketListener.
        1. 当事件发生时,这个类能够接收到通知,可用于多线程。
          Socket* socket() const;//返回正在监听的socket
          void setSocket(Socket* socket);//设置监听的socket
          virtual void stateChanged(SocketState::State newState) = 0;//纯虚函数,当socket状态改变时调用。
          virtual void messageReceived() = 0; //纯虚函数,当消息收到时调用
          virtual void error(std::string errorMessage) = 0;//纯虚函数,socket发生错误时调用
          
    • Socket.h
      • 定义了一个线程通信类Socket.
        SocketState::State state() const;//或者当前状态
        std::string errorString() const;//获得上一条信息
        void clearError();//清空错误队列
        void registerMessageType(int type, const google::protobuf::Message* messageType);//注册一条信息
        void addListener(SocketListener* listener); //增加监听者
        void removeListener(SocketListener* listener);//去掉监听者
        void connect(const std::string& address, int port); //连接
        void listen(const std::string& address, int port); //监听信息
        void sendMessage(MessagePtr message);//发送信息
        void close(); //关闭,停止处理任何信息。
        MessagePtr takeNextMessage(); //将下一条待处理信息从队列中取出
        void reset(); //当状态是关闭或者出错的时候调用,重置
        
    • Socket_p.h
      • socket通信的具体实现SocketPrivate,Socket.h封装了SocketPrivate.
        • 具体代码不说啦。下面有一个比较有意思的数据结构
             struct {
             Arcus::MessageState state;
             int type;
             int size;
             int size_recv;
             bool valid;
             char *data;
         } message;
             enum MessageState {
         MESSAGE_STATE_INIT,
         MESSAGE_STATE_HEADER,
         MESSAGE_STATE_SIZE,
         MESSAGE_STATE_TYPE,
         MESSAGE_STATE_DATA,
         MESSAGE_STATE_DISPATCH,
        };
        
    • ArcsuExport.h
      • 动态链接库的导入导出配置

总结

+总之,这个库是为了能够在不同平台下都能实现前端和后端的数据通信。后面用到的应该是socket和socketListener这两个类。

Curaengine内部的commandSocket类

  1. 说明:
    • commandsocket.h中包含了utils/socket.h文件,其实并没有用到这个文件(还有utils/socket.cpp,功能与Arcus库重复了)。可能最新版本的会用到这两个文件(或者永久淘汰了?)
    • 事实上就只有CommandSocket类用到了Arcus库。
  2. commandsocket.h中定义了两个类。
    • Private类
      1. 有以下成员:
        fffProcessor* processor;  //数据处理器
        Arcus::Socket* socket;  //通信类
        int object_count;    //object数量
        int current_object_number;
        std::shared_ptr<Cura::SlicedObjectList> slicedObjectList; //protobuf的
        Cura::SlicedObject* currentSlicedObject;  //当前切片对象,id和layer两个属性
        int slicedObjects;        //切片物体数
        std::vector<int64_t> objectIds;
        std::string tempGCodeFile;
        std::ostringstream gcode_output_stream;  //gcode输出流
        std::shared_ptr<PrintObject> objectToSlice; //指向待打印物体的指针
        
    • CommandSocket类
      1. 成员变量
        • Private的实例指针d。
      2. 方法
        CommandSocket(fffProcessor* processor);//新加数据处理器
        void connect(const std::string& ip, int port);//连接并且注册消息类型
        void handleObjectList(Cura::ObjectList* list);//d->objectToSlice->meshes添加数据
        void handleSettingList(Cura::SettingList* list); //为处理器配置参数
        void sendLayerInfo(int layer_nr, int32_t z, int32_t height);//弃用
        void sendPolygons(cura::PolygonType type, int layer_nr, cura::Polygons& polygons);//发送某层的多边形信息。
        void sendProgress(float amount);//发送打印进度
        void sendPrintTime();//向接收端发送打印信息
        void sendPrintMaterialForObject(int index, int extruder_nr, float material_amount);//弃用
        void beginSendSlicedObject();//发送开始切片
        void endSendSlicedObject();//发送结束切片
        void beginGCode();//生成gcode之前,设置gcodewriter输出流
        void sendGCodeLayer();//发送一层的gcode
        void sendGCodePrefix(std::string prefix);//发送gcode前缀代码
        
      3. commandsocket类其实主要也是为了与前端通信用的。
发表在 CuraEngine小白学习笔记| 留下评论

google的protobuf

Table of Contents

扯淡

  1. 为什么要学protobuf ?
    • 因为代码里用到了! 谷歌碧池.png就是里面这一坨,真是醉了。
  2. protobuf是什么鬼?

protobuf语法

  1. 概述
    • 这里简单介绍代码中用到的语法,详细语法见上链接或google官网。
  2. .proto 文件
    • 首先我们需要编写一个 .proto 文件,定义我们程序中需要处理的结构化数据。然后通过编译器可以将定义的数据结构转换成c++源文件。
      1. 结构化数据被称为 Message。包定义成package。
        package lm; 
        message helloworld 
        { 
           required int32     id = 1;  // ID 
           required string    str = 2;  // str 
           optional int32     opt = 3;  //optional field 
        }
        

        package 名字叫做 lm,定义了一个消息 helloworld。 该消息有三个成员,类型为 int32 的 id,另一个为类型为 string 的成员 str。 opt 是一个可选的成员,即消息中可以不包含该成员。

        • 当转换成c++文件(变成lm.helloworld.pb.h和lm.helloworld.pb.cc)时,包lm变成命名空间,helloworld变成类,成员则变成私有成员变量。 并且新增了很多处理成员数据的标准方法。
      2. 指定字段规则 所指定的消息字段修饰符必须是如下之一:
        • required:一个格式良好的消息一定要含有1个这种字段。表示该值是必须要设置的;
        • optional:消息格式中该字段可以有0个或1个值(不超过1个)。
        • repeated:在一个格式良好的消息中,这种字段可以重复任意多次(包括0次)。重复的值的顺序会被保留。表示该值可以重复,相当于java中的List。
      3. 标量数据类型
        .proto c++ 备注
        double double  
        float float  
        int32 int32 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint32。
        int64 int64 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint64。
        uint32 uint32  
        uint64 uint64  
        sint32 int32 使用可变长编码方式。有符号的整型值。编码时比通常的int32高效。
        sint64 int64 使用可变长编码方式。有符号的整型值。编码时比通常的int64高效。
        bool bool  
        string string 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。
        bytes string 可能包含任意顺序的字节数据。
      4. 枚举
        enum Corpus { //枚举常量必须在32位整型值的范围内
        
            UNIVERSAL = 0;
        
            WEB = 1;
        
            IMAGES = 2;
        
            LOCAL = 3;
        
            NEWS = 4;
        
            PRODUCTS = 5;
        
            VIDEO = 6;
        
          }
        
      5. 注释
        • 向.proto文件添加注释,可以使用C/C++/java风格的双斜杠(//) 语法格式
            message SearchRequest {
          
            required string query = 1;
          
            optional int32 page_number = 2;// 最终返回的页数
          
            optional int32 result_per_page = 3;// 每页返回的结果数
          
          }
          

源码

  • 现在来看看CuraEngine中得protobuf代码。如下:
    syntax = "proto3";
    
    package Cura;
    
    // typeid 1
    message ObjectList {
        repeated Object objects = 1;
    }
    
    message Object {
        int64 id = 1;
        bytes vertices = 2; //An array of 3 floats.
        bytes normals = 3; //An array of 3 floats.
        bytes indices = 4; //An array of ints.
        repeated Setting settings = 5; // Setting override per object, overruling the global settings.
    }
    
    // typeid 3
    message Progress {
        float amount = 1;
    }
    
    // typeid 2
    message SlicedObjectList {
        repeated SlicedObject objects = 1;
    }
    
    message SlicedObject {
        int64 id = 1;
    
        repeated Layer layers = 2;
    }
    
    message Layer {
        int32 id = 1;
        repeated Polygon polygons = 2;
    }
    
    message Polygon {
        enum Type {
            NoneType = 0;
            Inset0Type = 1;
            InsetXType = 2;
            SkinType = 3;
            SupportType = 4;
            SkirtType = 5;
        }
        Type type = 1;
        bytes points = 2;
    }
    
    // typeid 4
    message GCodeLayer {
        int64 id = 1;
        bytes data = 2;
    }
    
    // typeid 5
    message ObjectPrintTime {
        int64 id = 1;
        float time = 2;
        float material_amount = 3;
    }
    
    // typeid 6
    message SettingList {
        repeated Setting settings = 1;
    }
    
    message Setting {
        string name = 1;
    
        bytes value = 2;
    }
    
    // typeid 7
    message GCodePrefix {
        bytes data = 2;
    }
    
    • 不得不说,protobuf是很好的,这么几行就定义所有内容(MD那还用json写配置文件作死啊)
    • 编译方式
      • protobuf的头文件放在根目录的protobuf目录中,库文件则需要自己编译好,这里放在根目录,protc.lib、libprotoc.lib……。
      • 由于libArcus也要用到protobuf,生成protobuf库后先和Arcus项目放在一起,通过cmake配置,生成vs项目的libArcus。 编译libArcus,生成Arucs.dll。
      • 再注册Arcus.dll,将arcus和protobuf的头文件放在CuraEngine项目里,通过cmake配置,生成vs项目的CuraEngine。
      • 定义的Cura.proto文件编译时,查找protobuf头文件,调用protoc.lib,生成根目录下的Cura.pb.h和Cura.pb.cc。
    • 定义的类型
      1. Setting类。
        • 配置有关。
      2. Object类。
        • 有id、vetices、normals、indices、settings。
      3. OBjectlist类。
        • objects,多个Object对象集合。
      4. Progress类
        • amount。
      5. Slicedobject类
        • id、layer。
      6. SLicedobjectlist类。
        • objects。
      7. layer类
        • id、polygons。一个切片层。
      8. Polygon类
        • type(多边形的类型,是表面、支撑还是其他……) 、 points(顶点数)
      9. GCodelayer类
        • id,data字符串。
      10. OBjectprinttime类。
        • id、time、matrail_amount。
      11. GCodeprefix类。
        • data字符串。
    • 自注。
      • 上面定义的结构用处还不是很清楚,以后分析切片代码的时候再具体讲。
发表在 CuraEngine小白学习笔记| 留下评论

json 文件格式

Table of Contents

概述

  • JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。json
  • 数据结构:
    1. 对象是一个无序的“‘名称/值’对”集合。
      1. 一个对象以“{”(左括号)开始,“}”(右括号)结束。
      2. 每个“名称”后跟一个“:”(冒号);
      3. “‘名称/值’ 对”之间使用“,”(逗号)分隔。
    2. 数组是值(value)的有序集合。
      1. 一个数组以“[”(左中括号)开始,“]”(右中括号)结束。
      2. 值之间使用“,”(逗号)分隔。
    3. 值(value)可以是双引号括起来的字符串(string)、数值(number)、true、false、null、对象(object)或者数组(array)。这些结构可以嵌套。
    4. 字符串(string)是由双引号包围的任意数量 Unicode 字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。 字符串(string)与 C 或者 Java 的字符串非常相似。
    5. 数值(number)也与 C 或者 Java 的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编码细节。

参数

  • 通过 Cura 图形界面生成了配置文件(需要 Cura 15.06 版本或以上,生成在 resources/settings/中),也可以直接修改默认的 fdmprinter.json。
    1. layer_height 层高
    2. shell thickness 侧面壳厚
    3. fill density 内部填充百分比
    4. printspeed 打印速度
    5. support 支撑
    6. support type 支撑类型
      • none 不使用支撑
      • Touching buildplate 仅支撑底面
      • everywhere 所有斜侧面都产生支撑
    7. platform adhesion type 最底层与工作台粘合的形态
      • none 直接粘合
      • brim 产生延伸的边缘
      • raft 产生栈板
    8. filament 塑胶线材
    9. flow 挤出量微调
    10. 其他详细内容见 https://fhln.blog.ustc.edu.cn/?p=245
发表在 CuraEngine小白学习笔记| 一条评论

CuraEngine 基础功能类介绍

概述

  • 出来混总是要还的,这里不得不开始介绍底层最无聊的类。

点类和矩阵类

  1. Point3 类(定义在 intpoint.h)
    • 该类定义了点得三维坐标,坐标值是三个 int32 类型,x y z
    • 定义加、减、数乘、数除 d、点积、相等、不等 方法,不讲。

      int32_t max() //返回 x y z 中最大值
      bool testLength(int32_t len) //顶点距离原点值不能大于 lenth
      int64_t vSize2() const //离原点距离的平方
      int32_t vSize() const //离原点距离
      
  2. IntPoint 类(intpoint.h) 和 Point
    • 这个类是用在 ClipperLib 中的。Clipperlib 处理二维封闭图形。
    • 因此有两个变量 x 和 y。
    • 该类方法基本与 Point3 一样。

      INLINE int angle(const Point& p) //返回与 x 正半轴夹角,角度制
      INLINE Point crossZ(const Point& p0) //绕原点逆时针旋转 90 度
      {
           return Point(-p0.Y, p0.X);
      }
      
  3. PointMatrix 类(intpoint.h)
    1. 成员变量 matrix[ 4].是一个二维矩阵。
  4. 默认初始化单位阵,

    PointMatrix(double rotation) //对单位阵旋转一定角度。
          Point apply(const Point p) const //点 p 左乘矩阵
          Point unapply(const Point p) const //点 p 右乘矩阵
    
  5. FPoint3 类(floatpoint.h)
    • 基本与 Point3 完全一样,用 float 类型

      inline FPoint3 normalized()//正则化,单位化
      
         FPoint3 cross(const FPoint3& p)  //未知
      {
          return FPoint3(
              y*p.z-z*p.y,
              z*p.x-x*p.z,
              x*p.y-y*p.x);
      }
      
  6. FMatrix3x3 类(floatpoint.h)
    • 跟 PointMatrix 一样。
  7. vSize2f 函数

    INLINE float vSize2f(const Point& p0) //返回 p0 的距离平方
    
  8. vSize2 函数

    INLINE float vSize2(const Point& p0) //返回 p0 的距离
    
  9. vSizeMM 函数 +返回 mm 单位,默认是微米。

时间类

  1. Timekeeper(gettime.h)
    • 只有 starttime 这个变量,
    • restart()方法重启定时器,返回所用时间。
  2. TimeEstimatecalculator 类(timeEstimate.h)

    • 这个类估算打印所需的时间。
    • 成员变量:
      1. Position 类
    • 定义打印点所处位置
      1. Block 类
    • 记录打印机参数。
      • 方法

        1. setPosition(Position newPos)
          • 设置打印位置
        2. reset()
          • 清空打印位置
        3. void plan(Position newPos, double feedRate)
          • 对新位置点,新的进料速度进行配置
        4. double TimeEstimateCalculator::calculate()
          • 返回运动时间
        5. 具体实现的代码还有很多看不懂,很多是计算速度,加速度的,并无任何卵用。
          •  :recalculate_tranpezoids 方法有笔误
        for(unsigned int n=0; n<blocks.size(); n--)  //TODO: 有问题
        

配置类

  1. SettingConfig
    • 直接从 json 文件 json 读取配置的类,Single setting data.有以下成员变量,跟 json 文件项一一对应。
      1. std::string label;
      2. std::string key;
      3. std::string type;
      4. std::string default_value;
      5. std::string unit;
      6. SettingConfig* parent;
      7. std::list<SettingConfig> children;
    • 各种 get,set 方法,
    • addchild 方法递归添加一个键值。
  2. CettingCategory
    • 包含多个子配置 Contains one or more children settings.
    • 基本与 SettingConfig 一样。
  3. SettingRegistry
    • 注册类。包含所有的配置设定 This registry contains all known setting keys.
    • 方法:

      static SettingRegistry* getInstance() { return &instance; } //得到该类的静态实例   
      bool settingExists(std::string key) const; // 根据 key 查找值是否存在
      const SettingConfig* getSettingConfig(std::string key); // 得到对应值
      bool settingsLoaded(); //有数据的话返回真
      bool loadJSON(std::string filename); //载入 json 文件数据
      
      • 这里的 loadJSON 方法用到了 libs/rapidjson/中的 json 库。
      • 从代码来看,读入的 json 文件参数有 machine_settings、mesh_settings(没找到)和 categories 三类。
      • 数据存在 categories 和 settings 中。settings 是 map 类型。以后用到的主要是 setitngs 中的数据。
  4. SettingsBase 类
    • 该类主要是 map 类型的 setting_valuse 成员变量。
    • 各种 get 方法用来读取对应的格式化键值。
    • 没什么好说的。

打印相关

  1. EFillmethod

         enum EFillMethod  //填充方法选项
    {
        Fill_Lines,
        Fill_Grid,
        Fill_Triangles,
        Fill_Concentric,
        Fill_ZigZag,
        Fill_None
    }
    
  2. Eplatformadhesion

         enum EPlatformAdhesion  //最底层与工作台的粘合形态
    {
        Adhesion_None, //直接粘合
        Adhesion_Brim,//产生延伸的边缘
        Adhesion_Raft //产生栈板
    };
    
  3. ESupportType

         enum ESupportType  //支撑类型
    { 
        Support_None, //不试用支撑
        Support_PlatformOnly,//仅支撑底面
        Support_Everywhere//所有斜侧面都产生支撑
    };
    
  4. MAX_SPARSE_COMBINE

    //Maximum number of sparse layers that can be combined into a single sparse 稀少的 extrusion.
      MAX_SPARSE_COMBINE 8 //稀疏挤出过程中最大的稀疏层数
    
  5. EGcodeflavor
    • 打印机类型,见前文。

Mesh 类相关

  • 补完之前的基础类,终于可以开始说 mesh 了。mesh 即网状物,是 stl 转换成的数据结构。
  • MeshVertex 类
    • 记录 mesh 中 vertex 顶点,相邻面片索引(face)。其实定义成 struct 更合适吧。
  • MeshFace 类
    • meshFace 是有三个顶点的面片模型,一个面片因为是三角形,所以邻接了三个其他的面片。

            /* A MeshFace is a 3 dimensional model triangle with 3 points. These points are already converted to integers
      A face has 3 connected faces, corresponding to its 3 edges.
      Note that a correct model may have more than 2 faces connected via a single edge!
      In such a case the face_index stored in connected_face_index is the one connected via the outside; see ASCII art below:
      : horizontal slice through vertical edge connected to four faces :*/
      
    • 成员变量

      int vertex_index[3]; //顶点索引,逆时针顺序
      int connected_face_index[3]; //相邻面片索引,跟顶点相同顺序
      

Mesh 类!

  • Mesh 类是 3d 模型的最基本表征方式(那 stl 呢?),它含有模型中所有的 MeshFace 面片。
    由于继承自 SettingBase,它可以有一些配置。

           /*!
    A Mesh is the most basic representation of a 3D model. It contains all the faces as MeshFaces.
    
    See MeshFace for the specifics of how/when faces are connected.
    */
    
  • 成员变量
    • vertices 
      所有的顶点。
    • faces 
      所有的面片。
  • 方法

     Mesh(SettingsBase* parent); //初始化的时候继承了配置
    void addFace(Point3& v0, Point3& v1, Point3& v2); //添加一个面片
    void clear(); //清除所有数据
    void finish(); //所有面片添加完成后,连接相邻面片
    Point3 my_min(); //假设打印空间为长方体,找出角落点(x,y,z 均最小的点)
    Point3 my_max(); //同上
    
    • 说明一下:min 和 max 函数在 SB 的 vs 下编译报错,被我改成 my_min()和 my_max();不过作者也真是,取名取什么不好弄个冲突的。
  • 私有方法

     int findIndexOfVertex(Point3& v); //返回指定顶点的索引号,或者新建一个顶点。
    int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx);//返回与 notFaceIdx 片面邻接的面片的索引
    //http://stackoverflow.com/questions/14066933/direct-way-of-computing-clockwise-angle-between-2-vectors
    
    • getFaceIdxWithPoints 有一个几何的算法来计算邻接面片。

多个 mesh

  1. PrintObject 类(modelFile.h)
    • 该类是多个 mesh 的集合(成员变量 meshes)。由于可能一次有多个 STL 文件会被打印。
    • 方法。\\一些跟 mesh 类似。

      void finalize(); //设置打印的偏移量,即设置整个模型的位置
      void offset(Point3 offset) //将模型按 offset 偏移
      bool loadMeshFromFile(PrintObject* object, const char* filename, FMatrix3x3& matrix);//载入 stl 到 object 中
      

next……

  • 至此,基础已经讲完 甜点.png
  • 可以开始吃正餐了。。 正餐.png
转自:https://fhln.blog.ustc.edu.cn/
阅读更多

没有更多推荐了,返回首页