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小白學習筆記|留下評論

切片算法(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小白學習筆記|7條評論

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……

     

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章