解析 .STL 文件最有效的策略是什么?
我的代码的一个关键部分是导入 .STL 文件(一种常见的 CAD 文件格式),这限制了整体性能。
.STL 文件格式总结如下 - https://en.wikipedia.org/wiki/STL_(file_format)
此应用程序需要使用 ASCII 格式。
通用格式为:
solid name
facet normal ni nj nk
outer loop
vertex v1x v1y v1z
vertex v2x v2y v2z
vertex v3x v3y v3z
endloop
endfacet
endsolid
但是,我注意到没有严格的格式要求。并且,导入函数必须执行最少量的错误检查。我已经做了一些性能测量(使用 chrono),对于 43,000 行的文件给出:
stl_import() - 1.177568 s
解析循环 - 3.894250 s
解析循环:
cout << "Importing " << stl_path << "... ";
auto file_vec = import_stl(stl_path);
for (auto& l : file_vec) {
trim(l);
if (solid_state) {
if (facet_state) {
if (starts_with(l, "vertex")) {
//---------ADD FACE----------//
l.erase(0, 6);
trim(l);
vector<string> strs;
split(strs, l, is_any_of(" "));
point p = { stod(strs[0]), stod(strs[1]), stod(strs[2]) };
facet_points.push_back(p);
//---------------------------//
}
else {
if (starts_with(l, "endfacet")) {
facet_state = false;
}
}
}
else {
if (starts_with(l, "facet")) {
facet_state = true;
//assert(facet_points.size() == 0);
//---------------------------//
// Normals can be ignored //
//---------------------------//
}
if (starts_with(l, "endsolid")) {
solid_state = false;
}
}
}
else {
if (starts_with(l, "solid")) {
solid_state = true;
}
}
if (facet_points.size() == 3) {
triangle facet(facet_points[0], facet_points[1], facet_points[2]);
stl_solid.add_facet(facet);
facet_points.clear();
//check normal
facet.normal();
}
}
stl_import函数是:
std::vector<std::string> import_stl(const std::string& file_path)
{
std::ifstream infile(file_path);
SkipBOM(infile);
std::vector<std::string> file_vec;
std::string line;
while (std::getline(infile, line))
{
file_vec.push_back(line);
}
return file_vec;
}
我搜索了优化文件读取的方法等。并且,我发现使用 mmap 可以提高文件读取速度。
这个问题是询问.STL 文件的最佳解析策略是什么?
如果没有可用于衡量时间花在哪里的数据,就很难确定什么真正提高了性能。一个像样的图书馆已经在做这项工作可能是最简单的方法。然而,当前的代码使用了一些可能很容易提高性能的方法。我发现了一些事情:
std::getline(infile >> std::ws, line)
,而不是首先读取空格,然后将其修剪掉:std::ws
操纵器会跳过前导空格。starts_with()
与字符串文字,我宁愿将每一行读入“命令”和行尾,并将命令与 std::string const
对象进行比较:而不是字符比较,它可能足以比较尺寸。而不是在空白处将
split()
放入std::string
中,我宁愿重置一个合适的流(可能是std::vector<std::string>
,但为了防止复制可能的自定义内存流)并直接从中读取:std::istringstream
这种方法的另一个优点是允许格式检查:我总是不信任收到的任何输入,即使它来自受信任的来源。
如果您坚持使用调整字符序列和/或将其拆分为子序列,我强烈建议使用
std::istringstream in; // declared outside the reading loop
// ...
point p;
in.clear(); // get rid of potentially existing errors
in.str(line);
if (in >> p.x >> p.y >> p.z) {
facet_points.push_back(p);
}
std::string_view
std::vector<std::string>
等结构定义输入函数,以便简单地从流中读取它们。根据您正在使用的系统,您可能需要在读取文件之前调用 point