cura-engine为开源3D打印软件cura的核心引擎代码,详细介绍参看github主页。现在学习的是一个简单版的https://github.com/repetier/CuraEngine,最新版https://github.com/Ultimaker/CuraEngine添加了很多功能,尤其通信部分有点难懂。本系列文章主要是为了保存本人阅读记忆,一面吃过就忘,所以完全按照阅读顺序编写,故稍有凌乱。下面直接进入学习。
首先看fffProcessor.h中的读取STL文件部分:
函数ProsessFile:功能:STL文件读取prepareModel、生成切片数据processSliceData、生成Gcode writeGcode。(log函数现在滤去,单独研究)
函数prepareModel:功能:STL文件读取、优化STL文件、生成LayerParts。涉及到的类:class SimpleModel、class SimpleVolume、class OptimizedModel、cura class Slicer、cura class SupportStorage。
文件读取的实现:
1 if (files.size() == 1 && files[0][0] == '$') 2 { 3 const char *input_filename = files[0].c_str(); 4 model = new SimpleModel(); 5 for(unsigned int n=0; input_filename[n]; n++) 6 { 7 model->volumes.push_back(SimpleVolume()); 8 SimpleVolume* volume = &model->volumes[model->volumes.size()-1]; 9 guiSocket.sendNr(GUI_CMD_REQUEST_MESH);10 11 int32_t vertexCount = guiSocket.recvNr();12 int pNr = 0;13 cura::log("Reading mesh from socket with %i vertexes\n", vertexCount);14 Point3 v[3];15 while(vertexCount)16 {17 float f[3];18 guiSocket.recvAll(f, 3 * sizeof(float));19 FPoint3 fp(f[0], f[1], f[2]);20 v[pNr++] = config.matrix.apply(fp);21 if (pNr == 3)22 {23 volume->addFace(v[0], v[1], v[2]);24 pNr = 0;25 }26 vertexCount--;27 }28 }29 }else{30 model = new SimpleModel();31 for(unsigned int i=0;i < files.size(); i++) {32 if(files[i] == "-")33 model->volumes.push_back(SimpleVolume());34 else {35 cura::log("Loading %s from disk...\n", files[i].c_str());36 SimpleModel *test = loadModelFromFile(model,files[i].c_str(), config.matrix);37 if(test == nullptr) { // error while reading occurred38 cura::logError("Failed to load model: %s\n", files[i].c_str());39 return false;40 }41 }42 }43 }
此处读入文件来源有两种,一是通过guiSocket从通信中接收的数据,二是从硬盘中读取STL文件,通信部分暂且不提,先学习后者。这就到了loadModelFromFile函数,此函数名字就已透露其作用-读文件。代码在文件modelFile.cpp中,如下:
1 SimpleModel* loadModelFromFile(SimpleModel *m,const char* filename, FMatrix3x3& matrix) 2 { 3 const char* ext = strrchr(filename, '.'); 4 if (ext && stringcasecompare(ext, ".stl") == 0) 5 { 6 return loadModelSTL(m,filename, matrix); 7 } 8 if (filename[0] == '#' && binaryMeshBlob != nullptr) 9 {10 while(*filename == '#')11 {12 filename++;13 14 m->volumes.push_back(SimpleVolume());15 SimpleVolume* vol = &m->volumes[m->volumes.size()-1];16 int32_t n, pNr = 0;17 if (fread(&n, 1, sizeof(int32_t), binaryMeshBlob) < 1)18 return nullptr;19 cura::log("Reading mesh from binary blob with %i vertexes\n", n);20 Point3 v[3];21 while(n)22 {23 float f[3];24 if (fread(f, 3, sizeof(float), binaryMeshBlob) < 1)25 return nullptr;26 FPoint3 fp(f[0], f[1], f[2]);27 v[pNr++] = matrix.apply(fp);28 if (pNr == 3)29 {30 vol->addFace(v[0], v[1], v[2]);31 pNr = 0;32 }33 n--;34 }35 }36 return m;37 }38 return nullptr;39 }
此处可以看出分两种情况,一是读取.stl后缀的STL文件,二是读取blob(binary large object)二进制大对象,blob同样来源与硬盘数据文件,至于到底是神马,还不知道。
1 m->volumes.push_back(SimpleVolume());2 SimpleVolume* vol = &m->volumes[m->volumes.size()-1];
代码作者超级喜欢这种先推入一个初始的几何体,然后在通过引用取出到vol,再赋值,貌似我一般都是相反的顺序,不知这种有何优势。下面还是看一看函数LoadModelSTL()吧,就在正上方。
1 SimpleModel* loadModelSTL(SimpleModel *m,const char* filename, FMatrix3x3& matrix) 2 { 3 FILE* f = fopen(filename, "r"); 4 char buffer[6]; 5 if (f == nullptr) 6 return nullptr; 7 8 if (fread(buffer, 5, 1, f) != 1) 9 {10 fclose(f);11 return nullptr;12 }13 fclose(f);14 15 buffer[5] = '\0';16 if (stringcasecompare(buffer, "solid") == 0)17 {18 SimpleModel* asciiModel = loadModelSTL_ascii(m, filename, matrix);19 if (!asciiModel)20 return nullptr;21 22 // This logic is used to handle the case where the file starts with23 // "solid" but is a binary file.24 if (m->volumes[m->volumes.size()-1].faces.size() < 1)25 {26 m->volumes.erase(m->volumes.end() - 1);27 return loadModelSTL_binary(m, filename, matrix);28 }29 return asciiModel;30 }31 return loadModelSTL_binary(m, filename, matrix);32 }
此函数同样分两种情况(看来码农都喜欢二分)。很简单,读入开头5个字母,如果是solid,则按Ascii读入,否则按照二进制读入。然而作者心思谨慎,开头是solid时也有二进制情况,所以检查一下读入最后一个几何体如果一个面都没有,那就错了呗,滚回用二进制重新读。
函数loadModelSTL_ascii(SimpleModel *m,const char* filename, FMatrix3x3& matrix)
和loadModelSTL_binary(SimpleModel *m,const char* filename, FMatrix3x3& matrix)是我们这部分的最后一站,代码不是很难,但有个小小的问题,matrix到底是干什么用的。这个matrix来自最初的函数loadModelFromFile(model,files[i].c_str(), config.matrix);config据我目前所知是整个系统一些参数设置的一个汇总类class ConfigSettings。这里面保存了大量的参数设置。这个matrix是的用途就是每个读入的坐标与其相乘,结果再存入model->volume.faces,就是将几何体坐标根据需要做了个变换,至于变换的作用,现在还不清楚。
至此,文件读入功能结束,文中还有一些问题没有解决,主要有log相关函数,socket相关函数和config相关,这些都是贯穿整个程序的,以后详细学习。