Save/Load repaired errors from/to a mesh statistics to/from a 3MF

This commit is contained in:
YuSanka 2021-10-06 10:53:42 +02:00
parent 47b54d5ce0
commit 1cc7b4ba97
5 changed files with 124 additions and 54 deletions

View file

@ -134,6 +134,13 @@ static constexpr const char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
static constexpr const char* SOURCE_IN_INCHES = "source_in_inches"; static constexpr const char* SOURCE_IN_INCHES = "source_in_inches";
static constexpr const char* SOURCE_IN_METERS = "source_in_meters"; static constexpr const char* SOURCE_IN_METERS = "source_in_meters";
static constexpr const char* MESH_STAT_EDGES_FIXED = "edges_fixed";
static constexpr const char* MESH_STAT_DEGENERATED_FACETS = "degenerate_facets";
static constexpr const char* MESH_STAT_FACETS_REMOVED = "facets_removed";
static constexpr const char* MESH_STAT_FACETS_RESERVED = "facets_reversed";
static constexpr const char* MESH_STAT_BACKWARDS_EDGES = "backwards_edges";
const unsigned int VALID_OBJECT_TYPES_COUNT = 1; const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
const char* VALID_OBJECT_TYPES[] = const char* VALID_OBJECT_TYPES[] =
{ {
@ -383,6 +390,7 @@ namespace Slic3r {
unsigned int first_triangle_id; unsigned int first_triangle_id;
unsigned int last_triangle_id; unsigned int last_triangle_id;
MetadataList metadata; MetadataList metadata;
RepairedMeshErrors mesh_stats;
VolumeMetadata(unsigned int first_triangle_id, unsigned int last_triangle_id) VolumeMetadata(unsigned int first_triangle_id, unsigned int last_triangle_id)
: first_triangle_id(first_triangle_id) : first_triangle_id(first_triangle_id)
@ -531,7 +539,9 @@ namespace Slic3r {
bool _handle_end_config_object(); bool _handle_end_config_object();
bool _handle_start_config_volume(const char** attributes, unsigned int num_attributes); bool _handle_start_config_volume(const char** attributes, unsigned int num_attributes);
bool _handle_start_config_volume_mesh(const char** attributes, unsigned int num_attributes);
bool _handle_end_config_volume(); bool _handle_end_config_volume();
bool _handle_end_config_volume_mesh();
bool _handle_start_config_metadata(const char** attributes, unsigned int num_attributes); bool _handle_start_config_metadata(const char** attributes, unsigned int num_attributes);
bool _handle_end_config_metadata(); bool _handle_end_config_metadata();
@ -1391,6 +1401,8 @@ namespace Slic3r {
res = _handle_start_config_object(attributes, num_attributes); res = _handle_start_config_object(attributes, num_attributes);
else if (::strcmp(VOLUME_TAG, name) == 0) else if (::strcmp(VOLUME_TAG, name) == 0)
res = _handle_start_config_volume(attributes, num_attributes); res = _handle_start_config_volume(attributes, num_attributes);
else if (::strcmp(MESH_TAG, name) == 0)
res = _handle_start_config_volume_mesh(attributes, num_attributes);
else if (::strcmp(METADATA_TAG, name) == 0) else if (::strcmp(METADATA_TAG, name) == 0)
res = _handle_start_config_metadata(attributes, num_attributes); res = _handle_start_config_metadata(attributes, num_attributes);
@ -1411,6 +1423,8 @@ namespace Slic3r {
res = _handle_end_config_object(); res = _handle_end_config_object();
else if (::strcmp(VOLUME_TAG, name) == 0) else if (::strcmp(VOLUME_TAG, name) == 0)
res = _handle_end_config_volume(); res = _handle_end_config_volume();
else if (::strcmp(MESH_TAG, name) == 0)
res = _handle_end_config_volume_mesh();
else if (::strcmp(METADATA_TAG, name) == 0) else if (::strcmp(METADATA_TAG, name) == 0)
res = _handle_end_config_metadata(); res = _handle_end_config_metadata();
@ -1845,12 +1859,43 @@ namespace Slic3r {
return true; return true;
} }
bool _3MF_Importer::_handle_start_config_volume_mesh(const char** attributes, unsigned int num_attributes)
{
IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id);
if (object == m_objects_metadata.end()) {
add_error("Cannot assign volume mesh to a valid object");
return false;
}
if (object->second.volumes.empty()) {
add_error("Cannot assign mesh to a valid olume");
return false;
}
ObjectMetadata::VolumeMetadata& volume = object->second.volumes.back();
int edges_fixed = get_attribute_value_int(attributes, num_attributes, MESH_STAT_EDGES_FIXED );
int degenerate_facets = get_attribute_value_int(attributes, num_attributes, MESH_STAT_DEGENERATED_FACETS);
int facets_removed = get_attribute_value_int(attributes, num_attributes, MESH_STAT_FACETS_REMOVED );
int facets_reversed = get_attribute_value_int(attributes, num_attributes, MESH_STAT_FACETS_RESERVED );
int backwards_edges = get_attribute_value_int(attributes, num_attributes, MESH_STAT_BACKWARDS_EDGES );
volume.mesh_stats = { edges_fixed, degenerate_facets, facets_removed, facets_reversed, backwards_edges };
return true;
}
bool _3MF_Importer::_handle_end_config_volume() bool _3MF_Importer::_handle_end_config_volume()
{ {
// do nothing // do nothing
return true; return true;
} }
bool _3MF_Importer::_handle_end_config_volume_mesh()
{
// do nothing
return true;
}
bool _3MF_Importer::_handle_start_config_metadata(const char** attributes, unsigned int num_attributes) bool _3MF_Importer::_handle_start_config_metadata(const char** attributes, unsigned int num_attributes)
{ {
IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id); IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id);
@ -1947,7 +1992,7 @@ namespace Slic3r {
// Remove the vertices, that are not referenced by any face. // Remove the vertices, that are not referenced by any face.
its_compactify_vertices(its, true); its_compactify_vertices(its, true);
TriangleMesh triangle_mesh(std::move(its)); TriangleMesh triangle_mesh(std::move(its), volume_data.mesh_stats);
if (m_version == 0) { if (m_version == 0) {
// if the 3mf was not produced by PrusaSlicer and there is only one instance, // if the 3mf was not produced by PrusaSlicer and there is only one instance,
@ -2970,6 +3015,15 @@ namespace Slic3r {
for (const std::string& key : volume->config.keys()) { for (const std::string& key : volume->config.keys()) {
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n"; stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n";
} }
// stores mesh's statistics
const RepairedMeshErrors& stats = volume->mesh().stats().repaired_errors;
stream << " <" << MESH_TAG << " ";
stream << MESH_STAT_EDGES_FIXED << "=\"" << stats.edges_fixed << "\" ";
stream << MESH_STAT_DEGENERATED_FACETS << "=\"" << stats.degenerate_facets << "\" ";
stream << MESH_STAT_FACETS_REMOVED << "=\"" << stats.facets_removed << "\" ";
stream << MESH_STAT_FACETS_RESERVED << "=\"" << stats.facets_reversed << "\" ";
stream << MESH_STAT_BACKWARDS_EDGES << "=\"" << stats.backwards_edges << "\"/>\n";
stream << " </" << VOLUME_TAG << ">\n"; stream << " </" << VOLUME_TAG << ">\n";
} }

View file

@ -1566,16 +1566,17 @@ void ModelObject::print_info() const
cout << "open_edges = " << mesh.stats().open_edges << endl; cout << "open_edges = " << mesh.stats().open_edges << endl;
if (mesh.stats().repaired()) { if (mesh.stats().repaired()) {
if (mesh.stats().degenerate_facets > 0) const RepairedMeshErrors& stats = mesh.stats().repaired_errors;
cout << "degenerate_facets = " << mesh.stats().degenerate_facets << endl; if (stats.degenerate_facets > 0)
if (mesh.stats().edges_fixed > 0) cout << "degenerate_facets = " << stats.degenerate_facets << endl;
cout << "edges_fixed = " << mesh.stats().edges_fixed << endl; if (stats.edges_fixed > 0)
if (mesh.stats().facets_removed > 0) cout << "edges_fixed = " << stats.edges_fixed << endl;
cout << "facets_removed = " << mesh.stats().facets_removed << endl; if (stats.facets_removed > 0)
if (mesh.stats().facets_reversed > 0) cout << "facets_removed = " << stats.facets_removed << endl;
cout << "facets_reversed = " << mesh.stats().facets_reversed << endl; if (stats.facets_reversed > 0)
if (mesh.stats().backwards_edges > 0) cout << "facets_reversed = " << stats.facets_reversed << endl;
cout << "backwards_edges = " << mesh.stats().backwards_edges << endl; if (stats.backwards_edges > 0)
cout << "backwards_edges = " << stats.backwards_edges << endl;
} }
cout << "number_of_parts = " << mesh.stats().number_of_parts << endl; cout << "number_of_parts = " << mesh.stats().number_of_parts << endl;
cout << "volume = " << mesh.volume() << endl; cout << "volume = " << mesh.volume() << endl;
@ -1616,11 +1617,7 @@ TriangleMeshStats ModelObject::get_object_stl_stats() const
// initialize full_stats (for repaired errors) // initialize full_stats (for repaired errors)
full_stats.open_edges += stats.open_edges; full_stats.open_edges += stats.open_edges;
full_stats.degenerate_facets += stats.degenerate_facets; full_stats.repaired_errors.merge(stats.repaired_errors);
full_stats.edges_fixed += stats.edges_fixed;
full_stats.facets_removed += stats.facets_removed;
full_stats.facets_reversed += stats.facets_reversed;
full_stats.backwards_edges += stats.backwards_edges;
// another used satistics value // another used satistics value
if (volume->is_model_part()) { if (volume->is_model_part()) {
@ -1637,7 +1634,7 @@ int ModelObject::get_mesh_errors_count(const int vol_idx /*= -1*/) const
if (vol_idx >= 0) if (vol_idx >= 0)
return this->volumes[vol_idx]->get_mesh_errors_count(); return this->volumes[vol_idx]->get_mesh_errors_count();
const TriangleMeshStats& stats = get_object_stl_stats(); const RepairedMeshErrors& stats = get_object_stl_stats().repaired_errors;
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
stats.facets_reversed + stats.backwards_edges; stats.facets_reversed + stats.backwards_edges;
@ -1709,7 +1706,7 @@ void ModelVolume::calculate_convex_hull()
int ModelVolume::get_mesh_errors_count() const int ModelVolume::get_mesh_errors_count() const
{ {
const TriangleMeshStats &stats = this->mesh().stats(); const RepairedMeshErrors &stats = this->mesh().stats().repaired_errors;
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
stats.facets_reversed + stats.backwards_edges; stats.facets_reversed + stats.backwards_edges;

View file

@ -66,8 +66,10 @@ TriangleMesh::TriangleMesh(const indexed_triangle_set &its) : its(its)
fill_initial_stats(this->its, m_stats); fill_initial_stats(this->its, m_stats);
} }
TriangleMesh::TriangleMesh(indexed_triangle_set &&its) : its(std::move(its)) TriangleMesh::TriangleMesh(indexed_triangle_set &&its, const RepairedMeshErrors& errors/* = RepairedMeshErrors()*/) : its(std::move(its))
{ {
if (errors.repaired())
m_stats.repaired_errors = errors;
fill_initial_stats(this->its, m_stats); fill_initial_stats(this->its, m_stats);
} }
@ -194,11 +196,12 @@ bool TriangleMesh::ReadSTLFile(const char* input_file, bool repair)
auto facets_w_3_bad_edge = stl.stats.number_of_facets - stl.stats.connected_facets_1_edge; auto facets_w_3_bad_edge = stl.stats.number_of_facets - stl.stats.connected_facets_1_edge;
m_stats.open_edges = stl.stats.backwards_edges + facets_w_1_bad_edge + facets_w_2_bad_edge * 2 + facets_w_3_bad_edge * 3; m_stats.open_edges = stl.stats.backwards_edges + facets_w_1_bad_edge + facets_w_2_bad_edge * 2 + facets_w_3_bad_edge * 3;
m_stats.edges_fixed = stl.stats.edges_fixed; m_stats.repaired_errors = { stl.stats.edges_fixed,
m_stats.degenerate_facets = stl.stats.degenerate_facets; stl.stats.degenerate_facets,
m_stats.facets_removed = stl.stats.facets_removed; stl.stats.facets_removed,
m_stats.facets_reversed = stl.stats.facets_reversed; stl.stats.facets_reversed,
m_stats.backwards_edges = stl.stats.backwards_edges; stl.stats.backwards_edges };
m_stats.number_of_parts = stl.stats.number_of_parts; m_stats.number_of_parts = stl.stats.number_of_parts;
stl_generate_shared_vertices(&stl, this->its); stl_generate_shared_vertices(&stl, this->its);

View file

@ -16,19 +16,7 @@ namespace Slic3r {
class TriangleMesh; class TriangleMesh;
class TriangleMeshSlicer; class TriangleMeshSlicer;
struct TriangleMeshStats { struct RepairedMeshErrors {
// Mesh metrics.
uint32_t number_of_facets = 0;
stl_vertex max = stl_vertex::Zero();
stl_vertex min = stl_vertex::Zero();
stl_vertex size = stl_vertex::Zero();
float volume = -1.f;
int number_of_parts = 0;
// Mesh errors, remaining.
int open_edges = 0;
// Mesh errors, fixed.
// How many edges were united by merging their end points with some other end points in epsilon neighborhood? // How many edges were united by merging their end points with some other end points in epsilon neighborhood?
int edges_fixed = 0; int edges_fixed = 0;
// How many degenerate faces were removed? // How many degenerate faces were removed?
@ -43,6 +31,36 @@ struct TriangleMeshStats {
// Edges shared by two triangles, oriented incorrectly. // Edges shared by two triangles, oriented incorrectly.
int backwards_edges = 0; int backwards_edges = 0;
void clear() { *this = RepairedMeshErrors(); }
RepairedMeshErrors merge(const RepairedMeshErrors& rhs) const {
RepairedMeshErrors out;
out.edges_fixed = this->edges_fixed + rhs.edges_fixed;
out.degenerate_facets = this->degenerate_facets + rhs.degenerate_facets;
out.facets_removed = this->facets_removed + rhs.facets_removed;
out.facets_reversed = this->facets_reversed + rhs.facets_reversed;
out.backwards_edges = this->backwards_edges + rhs.backwards_edges;
return out;
}
bool repaired() const { return degenerate_facets > 0 || edges_fixed > 0 || facets_removed > 0 || facets_reversed > 0 || backwards_edges > 0; }
};
struct TriangleMeshStats {
// Mesh metrics.
uint32_t number_of_facets = 0;
stl_vertex max = stl_vertex::Zero();
stl_vertex min = stl_vertex::Zero();
stl_vertex size = stl_vertex::Zero();
float volume = -1.f;
int number_of_parts = 0;
// Mesh errors, remaining.
int open_edges = 0;
// Mesh errors, fixed.
RepairedMeshErrors repaired_errors;
void clear() { *this = TriangleMeshStats(); } void clear() { *this = TriangleMeshStats(); }
TriangleMeshStats merge(const TriangleMeshStats &rhs) const { TriangleMeshStats merge(const TriangleMeshStats &rhs) const {
@ -59,17 +77,13 @@ struct TriangleMeshStats {
out.number_of_parts = this->number_of_parts + rhs.number_of_parts; out.number_of_parts = this->number_of_parts + rhs.number_of_parts;
out.open_edges = this->open_edges + rhs.open_edges; out.open_edges = this->open_edges + rhs.open_edges;
out.volume = this->volume + rhs.volume; out.volume = this->volume + rhs.volume;
out.edges_fixed = this->edges_fixed + rhs.edges_fixed; out.repaired_errors.merge(rhs.repaired_errors);
out.degenerate_facets = this->degenerate_facets + rhs.degenerate_facets;
out.facets_removed = this->facets_removed + rhs.facets_removed;
out.facets_reversed = this->facets_reversed + rhs.facets_reversed;
out.backwards_edges = this->backwards_edges + rhs.backwards_edges;
return out; return out;
} }
} }
bool manifold() const { return open_edges == 0; } bool manifold() const { return open_edges == 0; }
bool repaired() const { return degenerate_facets > 0 || edges_fixed > 0 || facets_removed > 0 || facets_reversed > 0 || backwards_edges > 0; } bool repaired() const { return repaired_errors.repaired(); }
}; };
class TriangleMesh class TriangleMesh
@ -79,7 +93,7 @@ public:
TriangleMesh(const std::vector<Vec3f> &vertices, const std::vector<Vec3i> &faces); TriangleMesh(const std::vector<Vec3f> &vertices, const std::vector<Vec3i> &faces);
TriangleMesh(std::vector<Vec3f> &&vertices, const std::vector<Vec3i> &&faces); TriangleMesh(std::vector<Vec3f> &&vertices, const std::vector<Vec3i> &&faces);
explicit TriangleMesh(const indexed_triangle_set &M); explicit TriangleMesh(const indexed_triangle_set &M);
explicit TriangleMesh(indexed_triangle_set &&M); explicit TriangleMesh(indexed_triangle_set &&M, const RepairedMeshErrors& repaired_errors = RepairedMeshErrors());
void clear() { this->its.clear(); this->m_stats.clear(); } void clear() { this->its.clear(); this->m_stats.clear(); }
bool ReadSTLFile(const char* input_file, bool repair = true); bool ReadSTLFile(const char* input_file, bool repair = true);
bool write_ascii(const char* output_file); bool write_ascii(const char* output_file);

View file

@ -405,16 +405,18 @@ std::pair<wxString, std::string> ObjectList::get_mesh_errors(const int obj_idx,
auto_repaired_info = format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors); auto_repaired_info = format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors);
tooltip += auto_repaired_info +":\n"; tooltip += auto_repaired_info +":\n";
if (stats.degenerate_facets > 0) const RepairedMeshErrors& repaired = stats.repaired_errors;
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d degenerate facet", "%1$d degenerate facets", stats.degenerate_facets), stats.degenerate_facets) + "\n";
if (stats.edges_fixed > 0) if (repaired.degenerate_facets > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d edge fixed", "%1$d edges fixed", stats.edges_fixed), stats.edges_fixed) + "\n"; tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d degenerate facet", "%1$d degenerate facets", repaired.degenerate_facets), repaired.degenerate_facets) + "\n";
if (stats.facets_removed > 0) if (repaired.edges_fixed > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet removed", "%1$d facets removed", stats.facets_removed), stats.facets_removed) + "\n"; tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d edge fixed", "%1$d edges fixed", repaired.edges_fixed), repaired.edges_fixed) + "\n";
if (stats.facets_reversed > 0) if (repaired.facets_removed > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet reversed", "%1$d facets reversed", stats.facets_reversed), stats.facets_reversed) + "\n"; tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet removed", "%1$d facets removed", repaired.facets_removed), repaired.facets_removed) + "\n";
if (stats.backwards_edges > 0) if (repaired.facets_reversed > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d backwards edge", "%1$d backwards edges", stats.backwards_edges), stats.backwards_edges) + "\n"; tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet reversed", "%1$d facets reversed", repaired.facets_reversed), repaired.facets_reversed) + "\n";
if (repaired.backwards_edges > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d backwards edge", "%1$d backwards edges", repaired.backwards_edges), repaired.backwards_edges) + "\n";
} }
if (!stats.manifold()) { if (!stats.manifold()) {
remaining_info = format_wxstr(_L_PLURAL("Remaining %1$d open edge", "Remaining %1$d open edges", stats.open_edges), stats.open_edges); remaining_info = format_wxstr(_L_PLURAL("Remaining %1$d open edge", "Remaining %1$d open edges", stats.open_edges), stats.open_edges);