fix: 3MF multi-object parsing — flush per object
CI / Build & Test (push) Failing after 32s
CI / Release Build (push) Failing after 31s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled

This commit is contained in:
茂之钳
2026-07-24 11:42:13 +00:00
parent d05dc3f7f3
commit e67b758566
+31 -55
View File
@@ -328,14 +328,33 @@ static std::vector<ThreeMFMesh> parse_model_xml(const std::string& xml) {
std::istringstream iss(xml);
std::string line;
// Parse all <object> blocks in the XML
// Per-object state
std::vector<std::array<double, 3>> verts;
std::vector<std::array<int, 3>> tris;
bool in_vertices = false, in_triangles = false;
std::string object_name;
auto add_current_object = [&]() {
if (verts.empty()) return;
std::vector<mesh::Point3D> pts;
pts.reserve(verts.size());
for (const auto& v : verts) pts.emplace_back(v[0], v[1], v[2]);
std::vector<std::array<int, 3>> faces;
for (const auto& t : tris) {
if (t[0]>=0 && (size_t)t[0]<verts.size()
&& t[1]>=0 && (size_t)t[1]<verts.size()
&& t[2]>=0 && (size_t)t[2]<verts.size())
faces.push_back(t);
}
ThreeMFMesh m;
m.mesh.build_from_triangles(pts, faces);
m.name = object_name;
results.push_back(std::move(m));
verts.clear();
tris.clear();
};
while (std::getline(iss, line)) {
// Trim whitespace
size_t start = 0;
while (start < line.size() && (line[start] == ' ' || line[start] == '\t' || line[start] == '\r'))
++start;
@@ -344,79 +363,36 @@ static std::vector<ThreeMFMesh> parse_model_xml(const std::string& xml) {
if (!line.empty() && line.back() == '\r') line.pop_back();
}
// New object: flush previous
if (line.find("<object ") != std::string::npos) {
add_current_object();
object_name = xml_get_attr(line, "name");
}
if (line.find("<vertices>") != std::string::npos) {
in_vertices = true;
continue;
}
if (line.find("</vertices>") != std::string::npos) {
in_vertices = false;
continue;
}
if (line.find("<triangles>") != std::string::npos) {
in_triangles = true;
continue;
}
if (line.find("</triangles>") != std::string::npos) {
in_triangles = false;
continue;
}
if (line.find("<vertices>") != std::string::npos) { in_vertices = true; continue; }
if (line.find("</vertices>") != std::string::npos) { in_vertices = false; continue; }
if (line.find("<triangles>") != std::string::npos) { in_triangles = true; continue; }
if (line.find("</triangles>") != std::string::npos) { in_triangles = false; continue; }
if (in_vertices && line.find("<vertex ") != std::string::npos) {
auto xs = xml_get_attr(line, "x");
auto ys = xml_get_attr(line, "y");
auto zs = xml_get_attr(line, "z");
if (!xs.empty() && !ys.empty() && !zs.empty()) {
if (!xs.empty() && !ys.empty() && !zs.empty())
verts.push_back({std::stod(xs), std::stod(ys), std::stod(zs)});
}
}
if (in_triangles && line.find("<triangle ") != std::string::npos) {
auto v1s = xml_get_attr(line, "v1");
auto v2s = xml_get_attr(line, "v2");
auto v3s = xml_get_attr(line, "v3");
if (!v1s.empty() && !v2s.empty() && !v3s.empty()) {
if (!v1s.empty() && !v2s.empty() && !v3s.empty())
tris.push_back({std::stoi(v1s), std::stoi(v2s), std::stoi(v3s)});
}
}
}
// Build mesh
std::vector<mesh::Point3D> pts;
pts.reserve(verts.size());
for (const auto& v : verts) {
pts.emplace_back(v[0], v[1], v[2]);
}
std::vector<std::array<int, 3>> faces;
faces.reserve(tris.size());
for (const auto& t : tris) {
// Validate indices
if (t[0] >= 0 && static_cast<size_t>(t[0]) < verts.size() &&
t[1] >= 0 && static_cast<size_t>(t[1]) < verts.size() &&
t[2] >= 0 && static_cast<size_t>(t[2]) < verts.size()) {
faces.push_back(t);
}
}
ThreeMFMesh m;
m.mesh.build_from_triangles(pts, faces);
m.name = object_name;
results.push_back(m);
// Reset for next object
verts.clear();
tris.clear();
object_name.clear();
pts.clear();
faces.clear();
// Re-parse for next objects? No, the function ends here.
// Actually this only works for the first object.
// For multi-object, need to detect <object> boundaries
// Flush final object
add_current_object();
return results;
}