diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm
index 8d2a79f2a4..d0696aab02 100644
--- a/lib/Slic3r/Test.pm
+++ b/lib/Slic3r/Test.pm
@@ -139,7 +139,6 @@ sub mesh {
my $mesh = Slic3r::TriangleMesh->new;
$mesh->ReadFromPerl($vertices, $facets);
- $mesh->repair;
$mesh->scale_xyz(Slic3r::Pointf3->new(@{$params{scale_xyz}})) if $params{scale_xyz};
$mesh->translate(@{$params{translate}}) if $params{translate};
return $mesh;
diff --git a/resources/icons/exclamation_manifold.svg b/resources/icons/exclamation_manifold.svg
new file mode 100644
index 0000000000..cd8ba59544
--- /dev/null
+++ b/resources/icons/exclamation_manifold.svg
@@ -0,0 +1,17 @@
+
+
+
diff --git a/resources/icons/white/exclamation_manifold.svg b/resources/icons/white/exclamation_manifold.svg
new file mode 100644
index 0000000000..a18590167c
--- /dev/null
+++ b/resources/icons/white/exclamation_manifold.svg
@@ -0,0 +1,17 @@
+
+
+
diff --git a/sandboxes/aabb-evaluation/aabb-evaluation.cpp b/sandboxes/aabb-evaluation/aabb-evaluation.cpp
index 9ec7451e50..1019ecf28b 100644
--- a/sandboxes/aabb-evaluation/aabb-evaluation.cpp
+++ b/sandboxes/aabb-evaluation/aabb-evaluation.cpp
@@ -212,8 +212,7 @@ int main(const int argc, const char *argv[])
return -1;
}
- mesh.repair();
- if (mesh.facets_count() == 0) {
+ if (mesh.empty()) {
std::cerr << "Error loading " << argv[1] << " . It is empty." << std::endl;
return -1;
}
diff --git a/sandboxes/meshboolean/MeshBoolean.cpp b/sandboxes/meshboolean/MeshBoolean.cpp
index 392d907074..c8649888fd 100644
--- a/sandboxes/meshboolean/MeshBoolean.cpp
+++ b/sandboxes/meshboolean/MeshBoolean.cpp
@@ -24,7 +24,6 @@ int main(const int argc, const char * argv[])
TriangleMesh input;
input.ReadSTLFile(argv[1]);
- input.repair();
Benchmark bench;
diff --git a/sandboxes/opencsg/Engine.cpp b/sandboxes/opencsg/Engine.cpp
index 53e3402948..d8f1d34647 100644
--- a/sandboxes/opencsg/Engine.cpp
+++ b/sandboxes/opencsg/Engine.cpp
@@ -409,7 +409,6 @@ void CSGDisplay::on_scene_updated(const Scene &scene)
interior.transform(po->trafo().inverse());
mshinst.merge(interior);
- mshinst.require_shared_vertices();
mi->transform_mesh(&mshinst);
@@ -417,14 +416,12 @@ void CSGDisplay::on_scene_updated(const Scene &scene)
auto center = bb.center().cast();
mshinst.translate(-center);
- mshinst.require_shared_vertices();
m_scene_cache.add_mesh(mshinst, OpenCSG::Intersection,
m_csgsettings.get_convexity());
}
for (const sla::DrainHole &holept : holedata) {
TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh());
- holemesh.require_shared_vertices();
m_scene_cache.add_mesh(holemesh, OpenCSG::Subtraction, 1);
}
}
diff --git a/sandboxes/opencsg/ShaderCSGDisplay.cpp b/sandboxes/opencsg/ShaderCSGDisplay.cpp
index 8ceb234be0..2413bad5bb 100644
--- a/sandboxes/opencsg/ShaderCSGDisplay.cpp
+++ b/sandboxes/opencsg/ShaderCSGDisplay.cpp
@@ -43,7 +43,6 @@ void ShaderCSGDisplay::on_scene_updated(const Scene &scene)
interior.transform(po->trafo().inverse());
mshinst.merge(interior);
- mshinst.require_shared_vertices();
mi->transform_mesh(&mshinst);
@@ -51,15 +50,11 @@ void ShaderCSGDisplay::on_scene_updated(const Scene &scene)
auto center = bb.center().cast();
mshinst.translate(-center);
- mshinst.require_shared_vertices();
add_mesh(mshinst);
}
- for (const sla::DrainHole &holept : holedata) {
- TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh());
- holemesh.require_shared_vertices();
- add_mesh(holemesh);
- }
+ for (const sla::DrainHole &holept : holedata)
+ add_mesh(sla::to_triangle_mesh(holept.to_mesh()));
}
repaint();
diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp
index 3490b81836..0da5e73808 100644
--- a/src/PrusaSlicer.cpp
+++ b/src/PrusaSlicer.cpp
@@ -397,7 +397,7 @@ int CLI::run(int argc, char **argv)
TriangleMesh mesh = model.mesh();
mesh.repair();
- TriangleMeshPtrs meshes = mesh.cut_by_grid(m_config.option("cut_grid")->value);
+ std::vector meshes = mesh.cut_by_grid(m_config.option("cut_grid")->value);
size_t i = 0;
for (TriangleMesh* m : meshes) {
Model out;
diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp
index e5491b1aae..8c3ab154ad 100644
--- a/src/admesh/connect.cpp
+++ b/src/admesh/connect.cpp
@@ -239,6 +239,7 @@ private:
return edge_a.facet_number != edge_b.facet_number && edge_a == edge_b;
}
+ // Connect edge_a with edge_b, update edge connection statistics.
static void record_neighbors(stl_file *stl, const HashEdge &edge_a, const HashEdge &edge_b)
{
// Facet a's neighbor is facet b
@@ -249,7 +250,7 @@ private:
stl->neighbors_start[edge_b.facet_number].neighbor[edge_b.which_edge % 3] = edge_a.facet_number; /* sets the .neighbor part */
stl->neighbors_start[edge_b.facet_number].which_vertex_not[edge_b.which_edge % 3] = (edge_a.which_edge + 2) % 3; /* sets the .which_vertex_not part */
- if (((edge_a.which_edge < 3) && (edge_b.which_edge < 3)) || ((edge_a.which_edge > 2) && (edge_b.which_edge > 2))) {
+ if ((edge_a.which_edge < 3 && edge_b.which_edge < 3) || (edge_a.which_edge > 2 && edge_b.which_edge > 2)) {
// These facets are oriented in opposite directions, their normals are probably messed up.
stl->neighbors_start[edge_a.facet_number].which_vertex_not[edge_a.which_edge % 3] += 3;
stl->neighbors_start[edge_b.facet_number].which_vertex_not[edge_b.which_edge % 3] += 3;
@@ -479,12 +480,13 @@ void stl_check_facets_exact(stl_file *stl)
void stl_check_facets_nearby(stl_file *stl, float tolerance)
{
- if ( (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets)
- && (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets)
- && (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)) {
+ assert(stl->stats.connected_facets_3_edge <= stl->stats.connected_facets_2_edge);
+ assert(stl->stats.connected_facets_2_edge <= stl->stats.connected_facets_1_edge);
+ assert(stl->stats.connected_facets_1_edge <= stl->stats.number_of_facets);
+
+ if (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)
// No need to check any further. All facets are connected.
return;
- }
HashTableEdges hash_table(stl->stats.number_of_facets);
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
@@ -514,22 +516,12 @@ void stl_remove_unconnected_facets(stl_file *stl)
/* Update list of connected edges */
stl_neighbors &neighbors = stl->neighbors_start[facet_number];
// Update statistics on unconnected triangle edges.
- switch ((neighbors.neighbor[0] == -1) + (neighbors.neighbor[1] == -1) + (neighbors.neighbor[2] == -1)) {
- case 0: // Facet has 3 neighbors
- -- stl->stats.connected_facets_3_edge;
- -- stl->stats.connected_facets_2_edge;
- -- stl->stats.connected_facets_1_edge;
- break;
- case 1: // Facet has 2 neighbors
- -- stl->stats.connected_facets_2_edge;
- -- stl->stats.connected_facets_1_edge;
- break;
- case 2: // Facet has 1 neighbor
- -- stl->stats.connected_facets_1_edge;
- case 3: // Facet has 0 neighbors
- break;
- default:
- assert(false);
+ switch (neighbors.num_neighbors()) {
+ case 3: -- stl->stats.connected_facets_3_edge; // fall through
+ case 2: -- stl->stats.connected_facets_2_edge; // fall through
+ case 1: -- stl->stats.connected_facets_1_edge; // fall through
+ case 0: break;
+ default: assert(false);
}
if (facet_number < int(-- stl->stats.number_of_facets)) {
@@ -555,20 +547,14 @@ void stl_remove_unconnected_facets(stl_file *stl)
auto remove_degenerate = [stl, remove_facet](int facet)
{
- // Update statistics on face connectivity.
- auto stl_update_connects_remove_1 = [stl](int facet_num) {
- //FIXME when decreasing 3_edge, should I increase 2_edge etc?
- switch ((stl->neighbors_start[facet_num].neighbor[0] == -1) + (stl->neighbors_start[facet_num].neighbor[1] == -1) + (stl->neighbors_start[facet_num].neighbor[2] == -1)) {
- case 0: // Facet has 3 neighbors
- -- stl->stats.connected_facets_3_edge; break;
- case 1: // Facet has 2 neighbors
- -- stl->stats.connected_facets_2_edge; break;
- case 2: // Facet has 1 neighbor
- -- stl->stats.connected_facets_1_edge; break;
- case 3: // Facet has 0 neighbors
- break;
- default:
- assert(false);
+ // Update statistics on face connectivity after one edge was disconnected on the facet "facet_num".
+ auto update_connects_remove_1 = [stl](int facet_num) {
+ switch (stl->neighbors_start[facet_num].num_neighbors()) {
+ case 0: assert(false); break;
+ case 1: -- stl->stats.connected_facets_1_edge; break;
+ case 2: -- stl->stats.connected_facets_2_edge; break;
+ case 3: -- stl->stats.connected_facets_3_edge; break;
+ default: assert(false);
}
};
@@ -604,9 +590,9 @@ void stl_remove_unconnected_facets(stl_file *stl)
// Update statistics on edge connectivity.
if ((neighbor[0] == -1) && (neighbor[1] != -1))
- stl_update_connects_remove_1(neighbor[1]);
+ update_connects_remove_1(neighbor[1]);
if ((neighbor[1] == -1) && (neighbor[0] != -1))
- stl_update_connects_remove_1(neighbor[0]);
+ update_connects_remove_1(neighbor[0]);
if (neighbor[0] >= 0) {
if (neighbor[1] >= 0) {
@@ -634,7 +620,7 @@ void stl_remove_unconnected_facets(stl_file *stl)
stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = vnot[0];
}
if (neighbor[2] >= 0) {
- stl_update_connects_remove_1(neighbor[2]);
+ update_connects_remove_1(neighbor[2]);
stl->neighbors_start[neighbor[2]].neighbor[(vnot[2] + 1) % 3] = -1;
}
@@ -652,11 +638,9 @@ void stl_remove_unconnected_facets(stl_file *stl)
++ i;
if (stl->stats.connected_facets_1_edge < (int)stl->stats.number_of_facets) {
- // remove completely unconnected facets
+ // There are some faces with no connected edge at all. Remove completely unconnected facets.
for (uint32_t i = 0; i < stl->stats.number_of_facets;)
- if (stl->neighbors_start[i].neighbor[0] == -1 &&
- stl->neighbors_start[i].neighbor[1] == -1 &&
- stl->neighbors_start[i].neighbor[2] == -1) {
+ if (stl->neighbors_start[i].num_neighbors() == 0) {
// This facet is completely unconnected. Remove it.
remove_facet(i);
assert(stl_validate(stl));
diff --git a/src/admesh/stl.h b/src/admesh/stl.h
index 33e2b9c946..8c30a6ae5d 100644
--- a/src/admesh/stl.h
+++ b/src/admesh/stl.h
@@ -79,8 +79,7 @@ struct stl_neighbors {
which_vertex_not[1] = -1;
which_vertex_not[2] = -1;
}
- int num_neighbors_missing() const { return (this->neighbor[0] == -1) + (this->neighbor[1] == -1) + (this->neighbor[2] == -1); }
- int num_neighbors() const { return 3 - this->num_neighbors_missing(); }
+ int num_neighbors() const { return 3 - ((this->neighbor[0] == -1) + (this->neighbor[1] == -1) + (this->neighbor[2] == -1)); }
// Index of a neighbor facet.
int neighbor[3];
@@ -92,28 +91,44 @@ struct stl_stats {
stl_stats() { memset(&header, 0, 81); }
char header[81];
stl_type type = (stl_type)0;
+ // Should always match the number of facets stored inside stl_file::facet_start.
uint32_t number_of_facets = 0;
+ // Bounding box.
stl_vertex max = stl_vertex::Zero();
stl_vertex min = stl_vertex::Zero();
stl_vertex size = stl_vertex::Zero();
float bounding_diameter = 0.f;
float shortest_edge = 0.f;
+ // After repair, the volume shall always be positive.
float volume = -1.f;
+ // Number of face edges connected to another face.
+ // Don't use this statistics after repair, use the connected_facets_1/2/3_edge instead!
int connected_edges = 0;
+ // Faces with >=1, >=2 and 3 edges connected to another face.
int connected_facets_1_edge = 0;
int connected_facets_2_edge = 0;
int connected_facets_3_edge = 0;
+ // Faces with 1, 2 and 3 open edges after exact chaining, but before repair.
int facets_w_1_bad_edge = 0;
int facets_w_2_bad_edge = 0;
int facets_w_3_bad_edge = 0;
+ // Number of faces read form an STL file.
int original_num_facets = 0;
+ // Number of edges connected one to another by snapping their end vertices.
int edges_fixed = 0;
+ // Number of faces removed because they were degenerated.
int degenerate_facets = 0;
+ // Total number of facets removed: Degenerate faces and unconnected faces.
int facets_removed = 0;
+ // Number of faces added by hole filling.
int facets_added = 0;
+ // Number of faces reversed because of negative volume or because one patch was connected to another patch with incompatible normals.
int facets_reversed = 0;
+ // Number of incompatible edges remaining after the patches were connected together and possibly their normals flipped.
int backwards_edges = 0;
+ // Number of triangles, which were flipped during the fixing process.
int normals_fixed = 0;
+ // Number of connected triangle patches.
int number_of_parts = 0;
void clear() { *this = stl_stats(); }
@@ -135,13 +150,11 @@ struct stl_file {
std::vector facet_start;
std::vector neighbors_start;
// Statistics
- stl_stats stats;
+ stl_stats stats;
};
struct indexed_triangle_set
{
- indexed_triangle_set() {}
-
void clear() { indices.clear(); vertices.clear(); }
size_t memsize() const {
@@ -149,9 +162,7 @@ struct indexed_triangle_set
}
std::vector indices;
- std::vector vertices;
- //FIXME add normals once we get rid of the stl_file from TriangleMesh completely.
- //std::vector normals
+ std::vector vertices;
bool empty() const { return indices.empty() || vertices.empty(); }
};
diff --git a/src/admesh/stl_io.cpp b/src/admesh/stl_io.cpp
index ddf377c781..26f5dc3212 100644
--- a/src/admesh/stl_io.cpp
+++ b/src/admesh/stl_io.cpp
@@ -205,11 +205,12 @@ bool stl_write_quad_object(stl_file *stl, char *file)
fprintf(fp, "CQUAD\n");
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
- switch (stl->neighbors_start[i].num_neighbors_missing()) {
- case 0: color = connect_color; break;
- case 1: color = uncon_1_color; break;
- case 2: color = uncon_2_color; break;
- default: color = uncon_3_color;
+ switch (stl->neighbors_start[i].num_neighbors()) {
+ case 0:
+ default: color = uncon_3_color; break;
+ case 1: color = uncon_2_color; break;
+ case 2: color = uncon_1_color; break;
+ case 3: color = connect_color; break;
}
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), stl->facet_start[i].vertex[0](2), color(0), color(1), color(2));
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), stl->facet_start[i].vertex[1](2), color(0), color(1), color(2));
diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp
index 177d8d708b..f3a1d59881 100644
--- a/src/libslic3r/AppConfig.cpp
+++ b/src/libslic3r/AppConfig.cpp
@@ -139,6 +139,9 @@ void AppConfig::set_defaults()
if (get("default_action_on_select_preset").empty())
set("default_action_on_select_preset", "none"); // , "transfer", "discard" or "save"
+ if (get("default_action_on_new_project").empty())
+ set("default_action_on_new_project", "none"); // , "keep(transfer)", "discard" or "save"
+
if (get("color_mapinulation_panel").empty())
set("color_mapinulation_panel", "0");
diff --git a/src/libslic3r/BlacklistedLibraryCheck.cpp b/src/libslic3r/BlacklistedLibraryCheck.cpp
index 76f675c707..938f542497 100644
--- a/src/libslic3r/BlacklistedLibraryCheck.cpp
+++ b/src/libslic3r/BlacklistedLibraryCheck.cpp
@@ -12,7 +12,7 @@ namespace Slic3r {
#ifdef WIN32
//only dll name with .dll suffix - currently case sensitive
-const std::vector BlacklistedLibraryCheck::blacklist({ L"NahimicOSD.dll", L"SS2OSD.dll" });
+const std::vector BlacklistedLibraryCheck::blacklist({ L"NahimicOSD.dll", L"SS2OSD.dll", L"amhook.dll", L"AMHook.dll" });
bool BlacklistedLibraryCheck::get_blacklisted(std::vector& names)
{
diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index 043f951efb..90fa9bfaed 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -305,8 +305,8 @@ namespace Slic3r {
struct Geometry
{
- std::vector vertices;
- std::vector triangles;
+ std::vector vertices;
+ std::vector triangles;
std::vector custom_supports;
std::vector custom_seam;
std::vector mmu_segmentation;
@@ -720,7 +720,7 @@ namespace Slic3r {
}
// use the geometry to create the volumes in the new model objects
- ObjectMetadata::VolumeMetadataList volumes(1, { 0, (unsigned int)geometry->triangles.size() / 3 - 1 });
+ ObjectMetadata::VolumeMetadataList volumes(1, { 0, (unsigned int)geometry->triangles.size() - 1 });
// for each instance after the 1st, create a new model object containing only that instance
// and copy into it the geometry
@@ -793,7 +793,7 @@ namespace Slic3r {
// config data not found, this model was not saved using slic3r pe
// add the entire geometry as the single volume to generate
- volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() / 3 - 1);
+ volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() - 1);
// select as volumes
volumes_ptr = &volumes;
@@ -1559,9 +1559,10 @@ namespace Slic3r {
{
// appends the vertex coordinates
// missing values are set equal to ZERO
- m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, X_ATTR));
- m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, Y_ATTR));
- m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, Z_ATTR));
+ m_curr_object.geometry.vertices.emplace_back(
+ m_unit_factor * get_attribute_value_float(attributes, num_attributes, X_ATTR),
+ m_unit_factor * get_attribute_value_float(attributes, num_attributes, Y_ATTR),
+ m_unit_factor * get_attribute_value_float(attributes, num_attributes, Z_ATTR));
return true;
}
@@ -1595,9 +1596,10 @@ namespace Slic3r {
// appends the triangle's vertices indices
// missing values are set equal to ZERO
- m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V1_ATTR));
- m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V2_ATTR));
- m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V3_ATTR));
+ m_curr_object.geometry.triangles.emplace_back(
+ get_attribute_value_int(attributes, num_attributes, V1_ATTR),
+ get_attribute_value_int(attributes, num_attributes, V2_ATTR),
+ get_attribute_value_int(attributes, num_attributes, V3_ATTR));
m_curr_object.geometry.custom_supports.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR));
m_curr_object.geometry.custom_seam.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR));
@@ -1886,7 +1888,7 @@ namespace Slic3r {
return false;
}
- unsigned int geo_tri_count = (unsigned int)geometry.triangles.size() / 3;
+ unsigned int geo_tri_count = (unsigned int)geometry.triangles.size();
unsigned int renamed_volumes_count = 0;
for (const ObjectMetadata::VolumeMetadata& volume_data : volumes) {
@@ -1897,77 +1899,50 @@ namespace Slic3r {
Transform3d volume_matrix_to_object = Transform3d::Identity();
bool has_transform = false;
-#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
- bool is_left_handed = false;
-#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
// extract the volume transformation from the volume's metadata, if present
for (const Metadata& metadata : volume_data.metadata) {
if (metadata.key == MATRIX_KEY) {
volume_matrix_to_object = Slic3r::Geometry::transform3d_from_string(metadata.value);
has_transform = ! volume_matrix_to_object.isApprox(Transform3d::Identity(), 1e-10);
-#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
- is_left_handed = Slic3r::Geometry::Transformation(volume_matrix_to_object).is_left_handed();
-#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
break;
}
}
// splits volume out of imported geometry
- TriangleMesh triangle_mesh;
- stl_file &stl = triangle_mesh.stl;
- unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1;
- stl.stats.type = inmemory;
- stl.stats.number_of_facets = (uint32_t)triangles_count;
- stl.stats.original_num_facets = (int)stl.stats.number_of_facets;
- stl_allocate(&stl);
-
- unsigned int src_start_id = volume_data.first_triangle_id * 3;
-
- for (unsigned int i = 0; i < triangles_count; ++i) {
- unsigned int ii = i * 3;
- stl_facet& facet = stl.facet_start[i];
- for (unsigned int v = 0; v < 3; ++v) {
- unsigned int tri_id = geometry.triangles[src_start_id + ii + v] * 3;
- if (tri_id + 2 >= geometry.vertices.size()) {
- add_error("Malformed triangle mesh");
+ std::vector faces(geometry.triangles.begin() + volume_data.first_triangle_id, geometry.triangles.begin() + volume_data.last_triangle_id + 1);
+ const size_t triangles_count = faces.size();
+ for (Vec3i face : faces)
+ for (unsigned int tri_id : face)
+ if (tri_id < 0 || tri_id >= geometry.vertices.size()) {
+ add_error("Found invalid vertex id");
return false;
}
- facet.vertex[v] = Vec3f(geometry.vertices[tri_id + 0], geometry.vertices[tri_id + 1], geometry.vertices[tri_id + 2]);
- }
- }
-
- stl_get_size(&stl);
- triangle_mesh.repair();
-
-#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
- // PrusaSlicer older than 2.4.0 saved mirrored volumes with reversed winding of the triangles
- // This caused the call to TriangleMesh::repair() to reverse all the facets because the calculated volume was negative
- if (is_left_handed && stl.stats.facets_reversed > 0 && stl.stats.facets_reversed == stl.stats.original_num_facets)
- stl.stats.facets_reversed = 0;
-#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
+ TriangleMesh triangle_mesh(std::move(geometry.vertices), std::move(faces));
if (m_version == 0) {
// if the 3mf was not produced by PrusaSlicer and there is only one instance,
// bake the transformation into the geometry to allow the reload from disk command
// to work properly
if (object.instances.size() == 1) {
- triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix());
+ triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false);
object.instances.front()->set_transformation(Slic3r::Geometry::Transformation());
+ //FIXME do the mesh fixing?
}
}
+ if (triangle_mesh.volume() < 0)
+ triangle_mesh.flip_triangles();
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
// stores the volume matrix taken from the metadata, if present
if (has_transform)
volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
- volume->calculate_convex_hull();
// recreate custom supports, seam and mmu segmentation from previously loaded attribute
volume->supported_facets.reserve(triangles_count);
volume->seam_facets.reserve(triangles_count);
volume->mmu_segmentation_facets.reserve(triangles_count);
- for (unsigned i=0; imesh().repaired)
- throw Slic3r::FileIOError("store_3mf() requires repair()");
- if (!volume->mesh().has_shared_vertices())
- throw Slic3r::FileIOError("store_3mf() requires shared vertices");
-
volumes_offsets.insert({ volume, Offsets(vertices_count) });
const indexed_triangle_set &its = volume->mesh().its;
@@ -2588,10 +2558,7 @@ namespace Slic3r {
if (volume == nullptr)
continue;
-#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
bool is_left_handed = volume->is_left_handed();
-#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
-
VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume);
assert(volume_it != volumes_offsets.end());
@@ -2606,7 +2573,6 @@ namespace Slic3r {
{
const Vec3i &idx = its.indices[i];
char *ptr = buf;
-#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
" v1=\"" << boost::spirit::int_ <<
"\" v2=\"" << boost::spirit::int_ <<
@@ -2614,15 +2580,6 @@ namespace Slic3r {
idx[is_left_handed ? 2 : 0] + volume_it->second.first_vertex_id,
idx[1] + volume_it->second.first_vertex_id,
idx[is_left_handed ? 0 : 2] + volume_it->second.first_vertex_id);
-#else
- boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
- " v1=\"" << boost::spirit::int_ <<
- "\" v2=\"" << boost::spirit::int_ <<
- "\" v3=\"" << boost::spirit::int_ << "\"",
- idx[0] + volume_it->second.first_vertex_id,
- idx[1] + volume_it->second.first_vertex_id,
- idx[2] + volume_it->second.first_vertex_id);
-#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
*ptr = '\0';
output_buffer += buf;
}
diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp
index 35b3e0cf43..9392485f62 100644
--- a/src/libslic3r/Format/AMF.cpp
+++ b/src/libslic3r/Format/AMF.cpp
@@ -244,11 +244,11 @@ struct AMFParserContext
// Map from obect name to object idx & instances.
std::map m_object_instances_map;
// Vertices parsed for the current m_object.
- std::vector m_object_vertices;
+ std::vector m_object_vertices;
// Current volume allocated for an amf/object/mesh/volume subtree.
ModelVolume *m_volume { nullptr };
// Faces collected for the current m_volume.
- std::vector m_volume_facets;
+ std::vector m_volume_facets;
// Transformation matrix of a volume mesh from its coordinate system to Object's coordinate system.
Transform3d m_volume_transform;
// Current material allocated for an amf/metadata subtree.
@@ -598,9 +598,7 @@ void AMFParserContext::endElement(const char * /* name */)
case NODE_TYPE_VERTEX:
assert(m_object);
// Parse the vertex data
- m_object_vertices.emplace_back((float)atof(m_value[0].c_str()));
- m_object_vertices.emplace_back((float)atof(m_value[1].c_str()));
- m_object_vertices.emplace_back((float)atof(m_value[2].c_str()));
+ m_object_vertices.emplace_back(float(atof(m_value[0].c_str())), float(atof(m_value[1].c_str())), float(atof(m_value[1].c_str())));
m_value[0].clear();
m_value[1].clear();
m_value[2].clear();
@@ -609,9 +607,7 @@ void AMFParserContext::endElement(const char * /* name */)
// Faces of the current volume:
case NODE_TYPE_TRIANGLE:
assert(m_object && m_volume);
- m_volume_facets.emplace_back(atoi(m_value[0].c_str()));
- m_volume_facets.emplace_back(atoi(m_value[1].c_str()));
- m_volume_facets.emplace_back(atoi(m_value[2].c_str()));
+ m_volume_facets.emplace_back(atoi(m_value[0].c_str()), atoi(m_value[1].c_str()), atoi(m_value[2].c_str()));
m_value[0].clear();
m_value[1].clear();
m_value[2].clear();
@@ -621,44 +617,36 @@ void AMFParserContext::endElement(const char * /* name */)
case NODE_TYPE_VOLUME:
{
assert(m_object && m_volume);
- TriangleMesh mesh;
- stl_file &stl = mesh.stl;
- stl.stats.type = inmemory;
- stl.stats.number_of_facets = int(m_volume_facets.size() / 3);
- stl.stats.original_num_facets = stl.stats.number_of_facets;
- stl_allocate(&stl);
-
- bool has_transform = ! m_volume_transform.isApprox(Transform3d::Identity(), 1e-10);
- for (size_t i = 0; i < m_volume_facets.size();) {
- stl_facet &facet = stl.facet_start[i/3];
- for (unsigned int v = 0; v < 3; ++v)
- {
- unsigned int tri_id = m_volume_facets[i++] * 3;
- if (tri_id < 0 || tri_id + 2 >= m_object_vertices.size()) {
+ // Verify validity of face indices.
+ for (Vec3i face : m_volume_facets)
+ for (unsigned int tri_id : face)
+ if (tri_id < 0 || tri_id >= m_object_vertices.size()) {
this->stop("Malformed triangle mesh");
return;
}
- facet.vertex[v] = Vec3f(m_object_vertices[tri_id + 0], m_object_vertices[tri_id + 1], m_object_vertices[tri_id + 2]);
- }
- }
- stl_get_size(&stl);
- mesh.repair();
- m_volume->set_mesh(std::move(mesh));
- // stores the volume matrix taken from the metadata, if present
- if (has_transform)
- m_volume->source.transform = Slic3r::Geometry::Transformation(m_volume_transform);
- if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART))
+
{
+ TriangleMesh triangle_mesh { std::move(m_object_vertices), std::move(m_volume_facets) };
+ if (triangle_mesh.volume() < 0)
+ triangle_mesh.flip_triangles();
+ m_volume->set_mesh(std::move(triangle_mesh));
+ }
+
+ // stores the volume matrix taken from the metadata, if present
+ if (bool has_transform = !m_volume_transform.isApprox(Transform3d::Identity(), 1e-10); has_transform)
+ m_volume->source.transform = Slic3r::Geometry::Transformation(m_volume_transform);
+
+ if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART)) {
m_volume->source.object_idx = (int)m_model.objects.size() - 1;
m_volume->source.volume_idx = (int)m_model.objects.back()->volumes.size() - 1;
m_volume->center_geometry_after_creation();
- }
- else
+ } else
// pass false if the mesh offset has been already taken from the data
m_volume->center_geometry_after_creation(m_volume->source.input_file.empty());
m_volume->calculate_convex_hull();
m_volume_facets.clear();
+ m_object_vertices.clear();
m_volume = nullptr;
break;
}
@@ -1187,10 +1175,6 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
int num_vertices = 0;
for (ModelVolume *volume : object->volumes) {
vertices_offsets.push_back(num_vertices);
- if (! volume->mesh().repaired)
- throw Slic3r::FileIOError("store_amf() requires repair()");
- if (! volume->mesh().has_shared_vertices())
- throw Slic3r::FileIOError("store_amf() requires shared vertices");
const indexed_triangle_set &its = volume->mesh().its;
const Transform3d& matrix = volume->get_matrix();
for (size_t i = 0; i < its.vertices.size(); ++i) {
diff --git a/src/libslic3r/Format/OBJ.cpp b/src/libslic3r/Format/OBJ.cpp
index cb7eb45493..54c373ce32 100644
--- a/src/libslic3r/Format/OBJ.cpp
+++ b/src/libslic3r/Format/OBJ.cpp
@@ -19,7 +19,8 @@ namespace Slic3r {
bool load_obj(const char *path, TriangleMesh *meshptr)
{
- if(meshptr == nullptr) return false;
+ if (meshptr == nullptr)
+ return false;
// Parse the OBJ file.
ObjParser::ObjData data;
@@ -31,84 +32,69 @@ bool load_obj(const char *path, TriangleMesh *meshptr)
// Count the faces and verify, that all faces are triangular.
size_t num_faces = 0;
size_t num_quads = 0;
- for (size_t i = 0; i < data.vertices.size(); ) {
+ for (size_t i = 0; i < data.vertices.size(); ++ i) {
+ // Find the end of face.
size_t j = i;
for (; j < data.vertices.size() && data.vertices[j].coordIdx != -1; ++ j) ;
- if (i == j)
- continue;
- size_t face_vertices = j - i;
- if (face_vertices != 3 && face_vertices != 4) {
- // Non-triangular and non-quad faces are not supported as of now.
- return false;
+ if (size_t num_face_vertices = j - i; num_face_vertices > 0) {
+ if (num_face_vertices > 4) {
+ // Non-triangular and non-quad faces are not supported as of now.
+ BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains polygons with more than 4 vertices.";
+ return false;
+ } else if (num_face_vertices < 3) {
+ // Non-triangular and non-quad faces are not supported as of now.
+ BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains polygons with less than 2 vertices.";
+ return false;
+ }
+ if (num_face_vertices == 4)
+ ++ num_quads;
+ ++ num_faces;
+ i = j;
}
- if (face_vertices == 4)
- ++ num_quads;
- ++ num_faces;
- i = j + 1;
}
- // Convert ObjData into STL.
- TriangleMesh &mesh = *meshptr;
- stl_file &stl = mesh.stl;
- stl.stats.type = inmemory;
- stl.stats.number_of_facets = uint32_t(num_faces + num_quads);
- stl.stats.original_num_facets = int(num_faces + num_quads);
- // stl_allocate clears all the allocated data to zero, all normals are set to zeros as well.
- stl_allocate(&stl);
- size_t i_face = 0;
- for (size_t i = 0; i < data.vertices.size(); ++ i) {
- if (data.vertices[i].coordIdx == -1)
- continue;
- stl_facet &facet = stl.facet_start[i_face ++];
- size_t num_normals = 0;
- stl_normal normal(stl_normal::Zero());
- for (unsigned int v = 0; v < 3; ++ v) {
- const ObjParser::ObjVertex &vertex = data.vertices[i++];
- memcpy(facet.vertex[v].data(), &data.coordinates[vertex.coordIdx*4], 3 * sizeof(float));
- if (vertex.normalIdx != -1) {
- normal(0) += data.normals[vertex.normalIdx*3];
- normal(1) += data.normals[vertex.normalIdx*3+1];
- normal(2) += data.normals[vertex.normalIdx*3+2];
- ++ num_normals;
- }
- }
- // Result of obj_parseline() call is not checked, thus not all vertices are necessarily finalized with coord_Idx == -1.
- if (i < data.vertices.size() && data.vertices[i].coordIdx != -1) {
- // This is a quad. Produce the other triangle.
- stl_facet &facet2 = stl.facet_start[i_face++];
- facet2.vertex[0] = facet.vertex[0];
- facet2.vertex[1] = facet.vertex[2];
- const ObjParser::ObjVertex &vertex = data.vertices[i++];
- memcpy(facet2.vertex[2].data(), &data.coordinates[vertex.coordIdx * 4], 3 * sizeof(float));
- if (vertex.normalIdx != -1) {
- normal(0) += data.normals[vertex.normalIdx*3];
- normal(1) += data.normals[vertex.normalIdx*3+1];
- normal(2) += data.normals[vertex.normalIdx*3+2];
- ++ num_normals;
- }
- if (num_normals == 4) {
- // Normalize an average normal of a quad.
- float len = facet.normal.norm();
- if (len > EPSILON) {
- normal /= len;
- facet.normal = normal;
- facet2.normal = normal;
- }
- }
- } else if (num_normals == 3) {
- // Normalize an average normal of a triangle.
- float len = facet.normal.norm();
- if (len > EPSILON)
- facet.normal = normal / len;
- }
+ // Convert ObjData into indexed triangle set.
+ indexed_triangle_set its;
+ size_t num_vertices = data.coordinates.size() / 4;
+ its.vertices.reserve(num_vertices);
+ its.indices.reserve(num_faces + num_quads);
+ for (size_t i = 0; i < num_vertices; ++ i) {
+ size_t j = i << 2;
+ its.vertices.emplace_back(data.coordinates[j], data.coordinates[j + 1], data.coordinates[j + 2]);
}
- stl_get_size(&stl);
- mesh.repair();
- if (mesh.facets_count() == 0) {
+ int indices[4];
+ for (size_t i = 0; i < data.vertices.size();)
+ if (data.vertices[i].coordIdx == -1)
+ ++ i;
+ else {
+ int cnt = 0;
+ while (i < data.vertices.size())
+ if (const ObjParser::ObjVertex &vertex = data.vertices[i ++]; vertex.coordIdx == -1) {
+ break;
+ } else {
+ assert(cnt < 4);
+ if (vertex.coordIdx < 0 || vertex.coordIdx >= its.vertices.size()) {
+ BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains invalid vertex index.";
+ return false;
+ }
+ indices[cnt ++] = vertex.coordIdx;
+ }
+ if (cnt) {
+ assert(cnt == 3 || cnt == 4);
+ // Insert one or two faces (triangulate a quad).
+ its.indices.emplace_back(indices[0], indices[1], indices[2]);
+ if (cnt == 4)
+ its.indices.emplace_back(indices[0], indices[2], indices[3]);
+ }
+ }
+
+ *meshptr = TriangleMesh(std::move(its));
+ if (meshptr->empty()) {
BOOST_LOG_TRIVIAL(error) << "load_obj: This OBJ file couldn't be read because it's empty. " << path;
return false;
}
-
+ if (meshptr->volume() < 0)
+ meshptr->flip_triangles();
return true;
}
diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp
deleted file mode 100644
index 586fbafb52..0000000000
--- a/src/libslic3r/Format/PRUS.cpp
+++ /dev/null
@@ -1,333 +0,0 @@
-#include
-#include
-
-#include
-
-#include "miniz_extension.hpp"
-
-#include
-
-#include "../libslic3r.h"
-#include "../Model.hpp"
-
-#include "PRUS.hpp"
-
-#if 0
-// Enable debugging and assert in this file.
-#define DEBUG
-#define _DEBUG
-#undef NDEBUG
-#endif
-
-#include
-
-namespace Slic3r
-{
-
-struct StlHeader
-{
- char comment[80];
- uint32_t nTriangles;
-};
-
-static_assert(sizeof(StlHeader) == 84, "StlHeader size not correct");
-
-// Buffered line reader to a string buffer.
-class LineReader
-{
-public:
- LineReader(std::vector &data) : m_buffer(data), m_pos(0), m_len((int)data.size()) {}
-
- const char* next_line() {
- // Skip empty lines.
- while (m_pos < m_len && (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n'))
- ++ m_pos;
- if (m_pos == m_len) {
- // End of file.
- return nullptr;
- }
- // The buffer is nonempty and it does not start with end of lines. Find the first end of line.
- int end = m_pos + 1;
- while (end < m_len && m_buffer[end] != '\r' && m_buffer[end] != '\n')
- ++ end;
- char *ptr_out = m_buffer.data() + m_pos;
- m_pos = end + 1;
- m_buffer[end] = 0;
- return ptr_out;
- }
-
- int next_line_scanf(const char *format, ...)
- {
- const char *line = next_line();
- if (line == nullptr)
- return -1;
- int result;
- va_list arglist;
- va_start(arglist, format);
- result = vsscanf(line, format, arglist);
- va_end(arglist);
- return result;
- }
-
-private:
- std::vector &m_buffer;
- int m_pos;
- int m_len;
-};
-
-static void extract_model_from_archive(
- // name of the model file
- const char *name,
- // path to the archive
- const char *path,
- // content of scene.xml
- const std::vector &scene_xml_data,
- // loaded data of this STL
- std::vector &data,
- // Model, to which the newly loaded objects will be added
- Model *model,
- // To map multiple STLs into a single model object for multi-material prints.
- std::map &group_to_model_object)
-{
- // Find the model entry in the XML data.
- char model_name_tag[1024];
- sprintf(model_name_tag, "", name);
- const char *model_xml = strstr(scene_xml_data.data(), model_name_tag);
- const char *zero_tag = "";
- const char *zero_xml = strstr(scene_xml_data.data(), zero_tag);
- Vec3d instance_rotation = Vec3d::Zero();
- Vec3d instance_scaling_factor = Vec3d::Ones();
- Vec3d instance_offset = Vec3d::Zero();
- bool trafo_set = false;
- unsigned int group_id = (unsigned int)-1;
- unsigned int extruder_id = (unsigned int)-1;
- ModelObject *model_object = nullptr;
- if (model_xml != nullptr) {
- model_xml += strlen(model_name_tag);
- const char *position_tag = "";
- const char *position_xml = strstr(model_xml, position_tag);
- const char *rotation_tag = "";
- const char *rotation_xml = strstr(model_xml, rotation_tag);
- const char *scale_tag = "";
- const char *scale_xml = strstr(model_xml, scale_tag);
- float position[3], rotation[3], scale[3], zero[3];
- if (position_xml != nullptr && rotation_xml != nullptr && scale_xml != nullptr && zero_xml != nullptr &&
- sscanf(position_xml+strlen(position_tag),
- "[%f, %f, %f]", position, position+1, position+2) == 3 &&
- sscanf(rotation_xml+strlen(rotation_tag),
- "[%f, %f, %f]", rotation, rotation+1, rotation+2) == 3 &&
- sscanf(scale_xml+strlen(scale_tag),
- "[%f, %f, %f]", scale, scale+1, scale+2) == 3 &&
- sscanf(zero_xml+strlen(zero_tag),
- "[%f, %f, %f]", zero, zero+1, zero+2) == 3) {
- instance_scaling_factor = Vec3d((double)scale[0], (double)scale[1], (double)scale[2]);
- instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]);
- instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2]));
- trafo_set = true;
- }
- const char *group_tag = "";
- const char *group_xml = strstr(model_xml, group_tag);
- const char *extruder_tag = "";
- const char *extruder_xml = strstr(model_xml, extruder_tag);
- if (group_xml != nullptr) {
- int group = atoi(group_xml + strlen(group_tag));
- if (group > 0) {
- group_id = group;
- auto it = group_to_model_object.find(group_id);
- if (it != group_to_model_object.end())
- model_object = it->second;
- }
- }
- if (extruder_xml != nullptr) {
- int e = atoi(extruder_xml + strlen(extruder_tag));
- if (e > 0)
- extruder_id = e;
- }
- }
- if (! trafo_set)
- throw Slic3r::FileIOError(std::string("Archive ") + path + " does not contain a valid entry in scene.xml for " + name);
-
- // Extract the STL.
- StlHeader header;
- TriangleMesh mesh;
- bool mesh_valid = false;
- bool stl_ascii = false;
- if (data.size() > sizeof(StlHeader)) {
- memcpy((char*)&header, data.data(), sizeof(StlHeader));
- if (strncmp(header.comment, "solid ", 6) == 0)
- stl_ascii = true;
- else {
- // Header has been extracted. Now read the faces.
- stl_file &stl = mesh.stl;
- stl.stats.type = inmemory;
- stl.stats.number_of_facets = header.nTriangles;
- stl.stats.original_num_facets = header.nTriangles;
- stl_allocate(&stl);
- if (header.nTriangles > 0 && data.size() == 50 * header.nTriangles + sizeof(StlHeader)) {
- memcpy((char*)stl.facet_start.data(), data.data() + sizeof(StlHeader), 50 * header.nTriangles);
- if (sizeof(stl_facet) > SIZEOF_STL_FACET) {
- // The stl.facet_start is not packed tightly. Unpack the array of stl_facets.
- unsigned char *data = (unsigned char*)stl.facet_start.data();
- for (size_t i = header.nTriangles - 1; i > 0; -- i)
- memmove(data + i * sizeof(stl_facet), data + i * SIZEOF_STL_FACET, SIZEOF_STL_FACET);
- }
- // All the faces have been read.
- stl_get_size(&stl);
- mesh.repair();
- if (std::abs(stl.stats.min(2)) < EPSILON)
- stl.stats.min(2) = 0.;
- // Add a mesh to a model.
- if (mesh.facets_count() > 0)
- mesh_valid = true;
- }
- }
- } else
- stl_ascii = true;
-
- if (stl_ascii) {
- // Try to parse ASCII STL.
- char normal_buf[3][32];
- stl_facet facet;
- std::vector facets;
- LineReader line_reader(data);
- std::string solid_name;
- facet.extra[0] = facet.extra[1] = 0;
- for (;;) {
- const char *line = line_reader.next_line();
- if (line == nullptr)
- // End of file.
- break;
- if (strncmp(line, "solid", 5) == 0) {
- // Opening the "solid" block.
- if (! solid_name.empty()) {
- // Error, solid block is already open.
- facets.clear();
- break;
- }
- solid_name = line + 5;
- if (solid_name.empty())
- solid_name = "unknown";
- continue;
- }
- if (strncmp(line, "endsolid", 8) == 0) {
- // Closing the "solid" block.
- if (solid_name.empty()) {
- // Error, no solid block is open.
- facets.clear();
- break;
- }
- solid_name.clear();
- continue;
- }
- // Line has to start with the word solid.
- int res_normal = sscanf(line, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]);
- assert(res_normal == 3);
- int res_outer_loop = line_reader.next_line_scanf(" outer loop");
- assert(res_outer_loop == 0);
- int res_vertex1 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2));
- assert(res_vertex1 == 3);
- int res_vertex2 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2));
- assert(res_vertex2 == 3);
- int res_vertex3 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2));
- assert(res_vertex3 == 3);
- int res_endloop = line_reader.next_line_scanf(" endloop");
- assert(res_endloop == 0);
- int res_endfacet = line_reader.next_line_scanf(" endfacet");
- if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) {
- // perror("Something is syntactically very wrong with this ASCII STL!");
- facets.clear();
- break;
- }
- // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
- if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 ||
- sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 ||
- sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
- // Normal was mangled. Maybe denormals or "not a number" were stored?
- // Just reset the normal and silently ignore it.
- facet.normal = stl_normal::Zero();
- }
- facets.emplace_back(facet);
- }
- if (! facets.empty() && solid_name.empty()) {
- stl_file &stl = mesh.stl;
- stl.stats.type = inmemory;
- stl.stats.number_of_facets = (uint32_t)facets.size();
- stl.stats.original_num_facets = (int)facets.size();
- stl_allocate(&stl);
- memcpy((void*)stl.facet_start.data(), facets.data(), facets.size() * 50);
- stl_get_size(&stl);
- mesh.repair();
- // Add a mesh to a model.
- if (mesh.facets_count() > 0)
- mesh_valid = true;
- }
- }
-
- if (! mesh_valid)
- throw Slic3r::FileIOError(std::string("Archive ") + path + " does not contain a valid mesh for " + name);
-
- // Add this mesh to the model.
- ModelVolume *volume = nullptr;
- if (model_object == nullptr) {
- // This is a first mesh of a group. Create a new object & volume.
- model_object = model->add_object(name, path, std::move(mesh));
- volume = model_object->volumes.front();
- ModelInstance *instance = model_object->add_instance();
- instance->set_rotation(instance_rotation);
- instance->set_scaling_factor(instance_scaling_factor);
- instance->set_offset(instance_offset);
- if (group_id != (unsigned int)(-1))
- group_to_model_object[group_id] = model_object;
- } else {
- // This is not the 1st mesh of a group. Add it to the ModelObject.
- volume = model_object->add_volume(std::move(mesh));
- volume->name = name;
- }
- // Set the extruder to the volume.
- if (extruder_id != (unsigned int)-1)
- volume->config.set("extruder", int(extruder_id));
-}
-
-// Load a PrusaControl project file into a provided model.
-bool load_prus(const char *path, Model *model)
-{
- mz_zip_archive archive;
- mz_zip_zero_struct(&archive);
-
- size_t n_models_initial = model->objects.size();
- mz_bool res = MZ_FALSE;
- try {
- if (!open_zip_reader(&archive, path))
- throw Slic3r::FileIOError(std::string("Unable to init zip reader for ") + path);
- std::vector scene_xml_data;
- // For grouping multiple STLs into a single ModelObject for multi-material prints.
- std::map group_to_model_object;
- mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
- for (mz_uint i = 0; i < num_entries; ++ i) {
- mz_zip_archive_file_stat stat;
- if (! mz_zip_reader_file_stat(&archive, i, &stat))
- continue;
- std::vector buffer;
- buffer.assign((size_t)stat.m_uncomp_size, 0);
- res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (char*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
- if (res == MZ_FALSE)
- throw Slic3r::FileIOError(std::string("Error while extracting a file from ") + path);
- if (strcmp(stat.m_filename, "scene.xml") == 0) {
- if (! scene_xml_data.empty())
- throw Slic3r::FileIOError(std::string("Multiple scene.xml were found in the archive.") + path);
- scene_xml_data = std::move(buffer);
- } else if (boost::iends_with(stat.m_filename, ".stl")) {
- // May throw std::exception
- extract_model_from_archive(stat.m_filename, path, scene_xml_data, buffer, model, group_to_model_object);
- }
- }
- } catch (std::exception &ex) {
- close_zip_reader(&archive);
- throw ex;
- }
-
- close_zip_reader(&archive);
- return model->objects.size() > n_models_initial;
-}
-
-}; // namespace Slic3r
diff --git a/src/libslic3r/Format/PRUS.hpp b/src/libslic3r/Format/PRUS.hpp
deleted file mode 100644
index be5c5c61ac..0000000000
--- a/src/libslic3r/Format/PRUS.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#define slic3r_Format_PRUS_hpp_
-
-namespace Slic3r {
-
-class TriangleMesh;
-class Model;
-
-// Load a PrusaControl project file into a provided model.
-extern bool load_prus(const char *path, Model *model);
-
-}; // namespace Slic3r
diff --git a/src/libslic3r/Format/STL.cpp b/src/libslic3r/Format/STL.cpp
index 932906fe0e..2f2c9ec7fb 100644
--- a/src/libslic3r/Format/STL.cpp
+++ b/src/libslic3r/Format/STL.cpp
@@ -21,8 +21,7 @@ bool load_stl(const char *path, Model *model, const char *object_name_in)
// die "Failed to open $file\n" if !-e $path;
return false;
}
- mesh.repair();
- if (mesh.facets_count() == 0) {
+ if (mesh.empty()) {
// die "This STL file couldn't be read because it's empty.\n"
return false;
}
diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index d26e085e49..4e731c8b4e 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -343,18 +343,6 @@ void GCodeProcessor::TimeProcessor::reset()
machines[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
}
-struct FilePtr {
- FilePtr(FILE *f) : f(f) {}
- ~FilePtr() { this->close(); }
- void close() {
- if (this->f) {
- ::fclose(this->f);
- this->f = nullptr;
- }
- }
- FILE* f = nullptr;
-};
-
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector& moves, std::vector& lines_ends)
{
FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
@@ -755,11 +743,11 @@ void GCodeProcessor::Result::reset() {
#endif // ENABLE_GCODE_VIEWER_STATISTICS
const std::vector> GCodeProcessor::Producers = {
- { EProducer::PrusaSlicer, "PrusaSlicer" },
- { EProducer::Slic3rPE, "Slic3r Prusa Edition" },
- { EProducer::Slic3r, "Slic3r" },
+ { EProducer::PrusaSlicer, "generated by PrusaSlicer" },
+ { EProducer::Slic3rPE, "generated by Slic3r Prusa Edition" },
+ { EProducer::Slic3r, "generated by Slic3r" },
{ EProducer::Cura, "Cura_SteamEngine" },
- { EProducer::Simplify3D, "Simplify3D" },
+ { EProducer::Simplify3D, "G-Code generated by Simplify3D(R)" },
{ EProducer::CraftWare, "CraftWare" },
{ EProducer::ideaMaker, "ideaMaker" },
{ EProducer::KissSlicer, "KISSlicer" }
@@ -841,26 +829,16 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_result.extruders_count = extruders_count;
m_extruder_offsets.resize(extruders_count);
- for (size_t i = 0; i < extruders_count; ++i) {
- Vec2f offset = config.extruder_offset.get_at(i).cast();
- m_extruder_offsets[i] = { offset(0), offset(1), 0.0f };
- }
-
m_extruder_colors.resize(extruders_count);
- for (size_t i = 0; i < extruders_count; ++i) {
- m_extruder_colors[i] = static_cast(i);
- }
-
+ m_result.filament_diameters.resize(extruders_count);
+ m_result.filament_densities.resize(extruders_count);
m_extruder_temps.resize(extruders_count);
- m_result.filament_diameters.resize(config.filament_diameter.values.size());
- for (size_t i = 0; i < config.filament_diameter.values.size(); ++i) {
- m_result.filament_diameters[i] = static_cast(config.filament_diameter.values[i]);
- }
-
- m_result.filament_densities.resize(config.filament_density.values.size());
- for (size_t i = 0; i < config.filament_density.values.size(); ++i) {
- m_result.filament_densities[i] = static_cast(config.filament_density.values[i]);
+ for (size_t i = 0; i < extruders_count; ++ i) {
+ m_extruder_offsets[i] = to_3d(config.extruder_offset.get_at(i).cast().eval(), 0.f);
+ m_extruder_colors[i] = static_cast(i);
+ m_result.filament_diameters[i] = static_cast(config.filament_diameter.get_at(i));
+ m_result.filament_densities[i] = static_cast(config.filament_density.get_at(i));
}
if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) && config.machine_limits_usage.value != MachineLimitsUsage::Ignore) {
@@ -1201,6 +1179,16 @@ void GCodeProcessor::reset()
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
}
+static inline const char* skip_whitespaces(const char *begin, const char *end) {
+ for (; begin != end && (*begin == ' ' || *begin == '\t'); ++ begin);
+ return begin;
+}
+
+static inline const char* remove_eols(const char *begin, const char *end) {
+ for (; begin != end && (*(end - 1) == '\r' || *(end - 1) == '\n'); -- end);
+ return end;
+}
+
void GCodeProcessor::process_file(const std::string& filename, std::function cancel_callback)
{
CNumericLocalesSetter locales_setter;
@@ -1212,14 +1200,16 @@ void GCodeProcessor::process_file(const std::string& filename, std::function 1 && detect_producer(comment))
+ m_parser.parse_file_raw(filename, [this](GCodeReader& reader, const char *begin, const char *end) {
+ begin = skip_whitespaces(begin, end);
+ if (begin != end && *begin == ';') {
+ // Comment.
+ begin = skip_whitespaces(++ begin, end);
+ end = remove_eols(begin, end);
+ if (begin != end && detect_producer(std::string_view(begin, end - begin)))
m_parser.quit_parsing();
}
- });
+ });
m_parser.reset();
// if the gcode was produced by PrusaSlicer,
@@ -1384,9 +1374,11 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
};
BedSize bed_size;
+ bool producer_detected = false;
- m_parser.parse_file(filename, [this, &bed_size](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
- auto extract_double = [](const std::string& cmt, const std::string& key, double& out) {
+ m_parser.parse_file_raw(filename, [this, &bed_size, &producer_detected](GCodeReader& reader, const char* begin, const char* end) {
+
+ auto extract_double = [](const std::string_view cmt, const std::string& key, double& out) {
size_t pos = cmt.find(key);
if (pos != cmt.npos) {
pos = cmt.find(',', pos);
@@ -1398,12 +1390,12 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
return false;
};
- auto extract_floats = [](const std::string& cmt, const std::string& key, std::vector& out) {
+ auto extract_floats = [](const std::string_view cmt, const std::string& key, std::vector& out) {
size_t pos = cmt.find(key);
if (pos != cmt.npos) {
pos = cmt.find(',', pos);
if (pos != cmt.npos) {
- std::string data_str = cmt.substr(pos + 1);
+ const std::string_view data_str = cmt.substr(pos + 1);
std::vector values_str;
boost::split(values_str, data_str, boost::is_any_of("|,"), boost::token_compress_on);
for (const std::string& s : values_str) {
@@ -1414,28 +1406,39 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
}
return false;
};
-
- const std::string& comment = line.raw();
- if (comment.length() > 2 && comment.front() == ';') {
- if (bed_size.x == 0.0 && comment.find("strokeXoverride") != comment.npos)
- extract_double(comment, "strokeXoverride", bed_size.x);
- else if (bed_size.y == 0.0 && comment.find("strokeYoverride") != comment.npos)
- extract_double(comment, "strokeYoverride", bed_size.y);
- else if (comment.find("filamentDiameters") != comment.npos) {
- m_result.filament_diameters.clear();
- extract_floats(comment, "filamentDiameters", m_result.filament_diameters);
+
+ begin = skip_whitespaces(begin, end);
+ end = remove_eols(begin, end);
+ if (begin != end)
+ if (*begin == ';') {
+ // Comment.
+ begin = skip_whitespaces(++ begin, end);
+ if (begin != end) {
+ std::string_view comment(begin, end - begin);
+ if (producer_detected) {
+ if (bed_size.x == 0.0 && comment.find("strokeXoverride") != comment.npos)
+ extract_double(comment, "strokeXoverride", bed_size.x);
+ else if (bed_size.y == 0.0 && comment.find("strokeYoverride") != comment.npos)
+ extract_double(comment, "strokeYoverride", bed_size.y);
+ else if (comment.find("filamentDiameters") != comment.npos) {
+ m_result.filament_diameters.clear();
+ extract_floats(comment, "filamentDiameters", m_result.filament_diameters);
+ } else if (comment.find("filamentDensities") != comment.npos) {
+ m_result.filament_densities.clear();
+ extract_floats(comment, "filamentDensities", m_result.filament_densities);
+ } else if (comment.find("extruderDiameter") != comment.npos) {
+ std::vector extruder_diameters;
+ extract_floats(comment, "extruderDiameter", extruder_diameters);
+ m_result.extruders_count = extruder_diameters.size();
+ }
+ } else if (boost::starts_with(comment, "G-Code generated by Simplify3D(R)"))
+ producer_detected = true;
+ }
+ } else {
+ // Some non-empty G-code line detected, stop parsing config comments.
+ reader.quit_parsing();
}
- else if (comment.find("filamentDensities") != comment.npos) {
- m_result.filament_densities.clear();
- extract_floats(comment, "filamentDensities", m_result.filament_densities);
- }
- else if (comment.find("extruderDiameter") != comment.npos) {
- std::vector extruder_diameters;
- extract_floats(comment, "extruderDiameter", extruder_diameters);
- m_result.extruders_count = extruder_diameters.size();
- }
- }
- });
+ });
if (m_result.extruders_count == 0)
m_result.extruders_count = std::max(1, std::min(m_result.filament_diameters.size(), m_result.filament_densities.size()));
diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp
index 61ab10f223..7b106463a1 100644
--- a/src/libslic3r/GCodeReader.cpp
+++ b/src/libslic3r/GCodeReader.cpp
@@ -2,9 +2,11 @@
#include
#include
#include
+#include
#include
#include
#include
+#include "Utils.hpp"
#include "LocalesUtils.hpp"
@@ -126,44 +128,92 @@ void GCodeReader::update_coordinates(GCodeLine &gline, std::pair
+bool GCodeReader::parse_file_raw_internal(const std::string &filename, ParseLineCallback parse_line_callback, LineEndCallback line_end_callback)
{
- boost::nowide::ifstream f(file);
- f.sync_with_stdio(false);
+ FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
+
+ // Read the input stream 64kB at a time, extract lines and process them.
std::vector buffer(65536 * 10, 0);
- std::string line;
+ // Line buffer.
+ std::string gcode_line;
+ size_t file_pos = 0;
m_parsing = true;
- GCodeLine gline;
- while (m_parsing && ! f.eof()) {
- f.read(buffer.data(), buffer.size());
- if (! f.eof() && ! f.good())
- // Reading the input file failed.
+ for (;;) {
+ size_t cnt_read = ::fread(buffer.data(), 1, buffer.size(), in.f);
+ if (::ferror(in.f))
return false;
+ bool eof = cnt_read == 0;
auto it = buffer.begin();
- auto it_bufend = buffer.begin() + f.gcount();
- while (it != it_bufend) {
- bool eol = false;
+ auto it_bufend = buffer.begin() + cnt_read;
+ while (it != it_bufend || (eof && ! gcode_line.empty())) {
+ // Find end of line.
+ bool eol = false;
auto it_end = it;
- for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ;
- eol |= f.eof() && it_end == it_bufend;
+ for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end)
+ if (*it_end == '\n')
+ line_end_callback((it_end - buffer.begin()) + 1);
+ // End of line is indicated also if end of file was reached.
+ eol |= eof && it_end == it_bufend;
if (eol) {
- gline.reset();
- if (line.empty())
- this->parse_line(&(*it), &(*it_end), gline, callback);
+ if (gcode_line.empty())
+ parse_line_callback(&(*it), &(*it_end));
else {
- line.insert(line.end(), it, it_end);
- this->parse_line(line.c_str(), line.c_str() + line.size(), gline, callback);
- line.clear();
+ gcode_line.insert(gcode_line.end(), it, it_end);
+ parse_line_callback(gcode_line.c_str(), gcode_line.c_str() + gcode_line.size());
+ gcode_line.clear();
}
+ if (! m_parsing)
+ // The callback wishes to exit.
+ return true;
} else
- line.insert(line.end(), it, it_end);
- // Skip all the empty lines.
- for (it = it_end; it != it_bufend && (*it == '\r' || *it == '\n'); ++ it) ;
+ gcode_line.insert(gcode_line.end(), it, it_end);
+ // Skip EOL.
+ it = it_end;
+ if (it != it_bufend && *it == '\r')
+ ++ it;
+ if (it != it_bufend && *it == '\n') {
+ line_end_callback((it - buffer.begin()) + 1);
+ ++ it;
+ }
}
+ if (eof)
+ break;
+ file_pos += cnt_read;
}
return true;
}
+template
+bool GCodeReader::parse_file_internal(const std::string &filename, ParseLineCallback parse_line_callback, LineEndCallback line_end_callback)
+{
+ GCodeLine gline;
+ return this->parse_file_raw_internal(filename,
+ [this, &gline, parse_line_callback](const char *begin, const char *end) {
+ gline.reset();
+ this->parse_line(begin, end, gline, parse_line_callback);
+ },
+ line_end_callback);
+}
+
+bool GCodeReader::parse_file(const std::string &file, callback_t callback)
+{
+ return this->parse_file_internal(file, callback, [](size_t){});
+}
+
+bool GCodeReader::parse_file(const std::string &file, callback_t callback, std::vector &lines_ends)
+{
+ lines_ends.clear();
+ return this->parse_file_internal(file, callback, [&lines_ends](size_t file_pos){ lines_ends.emplace_back(file_pos); });
+}
+
+bool GCodeReader::parse_file_raw(const std::string &filename, raw_line_callback_t line_callback)
+{
+ return this->parse_file_raw_internal(filename,
+ [this, line_callback](const char *begin, const char *end) { line_callback(*this, begin, end); },
+ [](size_t){});
+}
+
bool GCodeReader::GCodeLine::has(char axis) const
{
const char *c = m_raw.c_str();
diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp
index 15376c0fc6..0ab268139f 100644
--- a/src/libslic3r/GCodeReader.hpp
+++ b/src/libslic3r/GCodeReader.hpp
@@ -76,6 +76,7 @@ public:
};
typedef std::function callback_t;
+ typedef std::function raw_line_callback_t;
GCodeReader() : m_verbose(false), m_extrusion_axis('E') { this->reset(); }
void reset() { memset(m_position, 0, sizeof(m_position)); }
@@ -114,6 +115,13 @@ public:
// Returns false if reading the file failed.
bool parse_file(const std::string &file, callback_t callback);
+ // Collect positions of line ends in the binary G-code to be used by the G-code viewer when memory mapping and displaying section of G-code
+ // as an overlay in the 3D scene.
+ bool parse_file(const std::string &file, callback_t callback, std::vector &lines_ends);
+ // Just read the G-code file line by line, calls callback (const char *begin, const char *end). Returns false if reading the file failed.
+ bool parse_file_raw(const std::string &file, raw_line_callback_t callback);
+
+ // To be called by the callback to stop parsing.
void quit_parsing() { m_parsing = false; }
float& x() { return m_position[X]; }
@@ -132,6 +140,11 @@ public:
// void set_extrusion_axis(char axis) { m_extrusion_axis = axis; }
private:
+ template
+ bool parse_file_raw_internal(const std::string &filename, ParseLineCallback parse_line_callback, LineEndCallback line_end_callback);
+ template
+ bool parse_file_internal(const std::string &filename, ParseLineCallback parse_line_callback, LineEndCallback line_end_callback);
+
const char* parse_line_internal(const char *ptr, const char *end, GCodeLine &gline, std::pair &command);
void update_coordinates(GCodeLine &gline, std::pair &command);
@@ -154,6 +167,7 @@ private:
char m_extrusion_axis;
float m_position[NUM_AXES];
bool m_verbose;
+ // To be set by the callback to stop parsing.
bool m_parsing{ false };
};
diff --git a/src/libslic3r/LocalesUtils.cpp b/src/libslic3r/LocalesUtils.cpp
index 8c42a36baf..d321072335 100644
--- a/src/libslic3r/LocalesUtils.cpp
+++ b/src/libslic3r/LocalesUtils.cpp
@@ -51,7 +51,7 @@ bool is_decimal_separator_point()
}
-double string_to_double_decimal_point(const std::string& str, size_t* pos /* = nullptr*/)
+double string_to_double_decimal_point(const std::string_view str, size_t* pos /* = nullptr*/)
{
double out;
size_t p = fast_float::from_chars(str.data(), str.data() + str.size(), out).ptr - str.data();
diff --git a/src/libslic3r/LocalesUtils.hpp b/src/libslic3r/LocalesUtils.hpp
index 113cfa04b0..f63c3572f7 100644
--- a/src/libslic3r/LocalesUtils.hpp
+++ b/src/libslic3r/LocalesUtils.hpp
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
#ifdef __APPLE__
#include
@@ -40,7 +41,7 @@ bool is_decimal_separator_point();
// (We use user C locales and "C" C++ locales in most of the code.)
std::string float_to_string_decimal_point(double value, int precision = -1);
//std::string float_to_string_decimal_point(float value, int precision = -1);
-double string_to_double_decimal_point(const std::string& str, size_t* pos = nullptr);
+double string_to_double_decimal_point(const std::string_view str, size_t* pos = nullptr);
} // namespace Slic3r
diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp
index a59165946a..25250e2341 100644
--- a/src/libslic3r/MeshBoolean.cpp
+++ b/src/libslic3r/MeshBoolean.cpp
@@ -29,18 +29,17 @@ TriangleMesh eigen_to_triangle_mesh(const EigenMesh &emesh)
{
auto &VC = emesh.first; auto &FC = emesh.second;
- Pointf3s points(size_t(VC.rows()));
- std::vector facets(size_t(FC.rows()));
+ indexed_triangle_set its;
+ its.vertices.reserve(size_t(VC.rows()));
+ its.indices.reserve(size_t(FC.rows()));
for (Eigen::Index i = 0; i < VC.rows(); ++i)
- points[size_t(i)] = VC.row(i);
+ its.vertices.emplace_back(VC.row(i).cast());
for (Eigen::Index i = 0; i < FC.rows(); ++i)
- facets[size_t(i)] = FC.row(i);
+ its.indices.emplace_back(FC.row(i));
- TriangleMesh out{points, facets};
- out.require_shared_vertices();
- return out;
+ return TriangleMesh { std::move(its) };
}
EigenMesh triangle_mesh_to_eigen(const TriangleMesh &mesh)
@@ -131,28 +130,27 @@ void triangle_mesh_to_cgal(const std::vector & V,
out.add_face(VI(f(0)), VI(f(1)), VI(f(2)));
}
-inline Vec3d to_vec3d(const _EpicMesh::Point &v)
+inline Vec3f to_vec3f(const _EpicMesh::Point& v)
{
- return {v.x(), v.y(), v.z()};
+ return { float(v.x()), float(v.y()), float(v.z()) };
}
-inline Vec3d to_vec3d(const _EpecMesh::Point &v)
+inline Vec3f to_vec3f(const _EpecMesh::Point& v)
{
CGAL::Cartesian_converter cvt;
auto iv = cvt(v);
- return {iv.x(), iv.y(), iv.z()};
+ return { float(iv.x()), float(iv.y()), float(iv.z()) };
}
template TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh)
{
- Pointf3s points;
- std::vector facets;
- points.reserve(cgalmesh.num_vertices());
- facets.reserve(cgalmesh.num_faces());
+ indexed_triangle_set its;
+ its.vertices.reserve(cgalmesh.num_vertices());
+ its.indices.reserve(cgalmesh.num_faces());
for (auto &vi : cgalmesh.vertices()) {
auto &v = cgalmesh.point(vi); // Don't ask...
- points.emplace_back(to_vec3d(v));
+ its.vertices.emplace_back(to_vec3f(v));
}
for (auto &face : cgalmesh.faces()) {
@@ -166,12 +164,10 @@ template TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh)
}
if (i == 3)
- facets.emplace_back(facet);
+ its.indices.emplace_back(facet);
}
- TriangleMesh out{points, facets};
- out.repair();
- return out;
+ return TriangleMesh(std::move(its));
}
std::unique_ptr
diff --git a/src/libslic3r/MeshSplitImpl.hpp b/src/libslic3r/MeshSplitImpl.hpp
index 7233ac40d6..c9df648fb9 100644
--- a/src/libslic3r/MeshSplitImpl.hpp
+++ b/src/libslic3r/MeshSplitImpl.hpp
@@ -28,65 +28,84 @@ template<> struct ItsWithNeighborsIndex_ {
}
};
-// Visit all unvisited neighboring facets that are reachable from the first unvisited facet,
-// and return them.
+// Discover connected patches of facets one by one.
template
-std::vector its_find_unvisited_neighbors(
- const indexed_triangle_set &its,
- const NeighborIndex & neighbor_index,
- std::vector & visited)
-{
- using stack_el = size_t;
-
- auto facestack = reserve_vector(its.indices.size());
- auto push = [&facestack] (const stack_el &s) { facestack.emplace_back(s); };
- auto pop = [&facestack] () -> stack_el {
- stack_el ret = facestack.back();
- facestack.pop_back();
- return ret;
- };
-
- // find the next unvisited facet and push the index
- auto facet = std::find(visited.begin(), visited.end(), false);
- std::vector ret;
-
- if (facet != visited.end()) {
- ret.reserve(its.indices.size());
- auto idx = size_t(facet - visited.begin());
- push(idx);
- ret.emplace_back(idx);
- visited[idx] = true;
+struct NeighborVisitor {
+ NeighborVisitor(const indexed_triangle_set &its, const NeighborIndex &neighbor_index) :
+ its(its), neighbor_index(neighbor_index) {
+ m_visited.assign(its.indices.size(), false);
+ m_facestack.reserve(its.indices.size());
+ }
+ NeighborVisitor(const indexed_triangle_set &its, NeighborIndex &&aneighbor_index) :
+ its(its), neighbor_index(m_neighbor_index_data), m_neighbor_index_data(std::move(aneighbor_index)) {
+ m_visited.assign(its.indices.size(), false);
+ m_facestack.reserve(its.indices.size());
}
- while (!facestack.empty()) {
- size_t facet_idx = pop();
- const auto &neighbors = neighbor_index[facet_idx];
- for (auto neighbor_idx : neighbors) {
- if (size_t(neighbor_idx) < visited.size() && !visited[size_t(neighbor_idx)]) {
- visited[size_t(neighbor_idx)] = true;
- push(stack_el(neighbor_idx));
- ret.emplace_back(size_t(neighbor_idx));
+ template
+ void visit(Visitor visitor)
+ {
+ // find the next unvisited facet and push the index
+ auto facet = std::find(m_visited.begin() + m_seed, m_visited.end(), false);
+ m_seed = facet - m_visited.begin();
+
+ if (facet != m_visited.end()) {
+ // Skip this element in the next round.
+ auto idx = m_seed ++;
+ if (! visitor(idx))
+ return;
+ this->push(idx);
+ m_visited[idx] = true;
+ while (! m_facestack.empty()) {
+ size_t facet_idx = this->pop();
+ for (auto neighbor_idx : neighbor_index[facet_idx]) {
+ assert(neighbor_idx < int(m_visited.size()));
+ if (neighbor_idx >= 0 && !m_visited[neighbor_idx]) {
+ if (! visitor(size_t(neighbor_idx)))
+ return;
+ m_visited[neighbor_idx] = true;
+ this->push(stack_el(neighbor_idx));
+ }
+ }
}
}
}
- return ret;
-}
+ const indexed_triangle_set &its;
+ const NeighborIndex &neighbor_index;
+
+private:
+ // If initialized with &&neighbor_index, take the ownership of the data.
+ const NeighborIndex m_neighbor_index_data;
+
+ std::vector m_visited;
+
+ using stack_el = size_t;
+ std::vector m_facestack;
+ void push(const stack_el &s) { m_facestack.emplace_back(s); }
+ stack_el pop() { stack_el ret = m_facestack.back(); m_facestack.pop_back(); return ret; }
+
+ // Last face visited.
+ size_t m_seed { 0 };
+};
} // namespace meshsplit_detail
+// Funky wrapper for timinig of its_split() using various neighbor index creating methods, see sandboxes/its_neighbor_index/main.cpp
template struct ItsNeighborsWrapper
{
using Index = IndexT;
- const indexed_triangle_set *its;
- IndexT index;
+ const indexed_triangle_set &its;
+ const IndexT &index_ref;
+ const IndexT index;
- ItsNeighborsWrapper(const indexed_triangle_set &m, IndexT &&idx)
- : its{&m}, index{std::move(idx)}
- {}
+ // Keeping a reference to index, the caller is responsible for keeping the index alive.
+ ItsNeighborsWrapper(const indexed_triangle_set &its, const IndexT &index) : its{its}, index_ref{index} {}
+ // Taking ownership of the index.
+ ItsNeighborsWrapper(const indexed_triangle_set &its, IndexT &&aindex) : its{its}, index_ref{index}, index(std::move(aindex)) {}
- const auto& get_its() const noexcept { return *its; }
- const auto& get_index() const noexcept { return index; }
+ const auto& get_its() const noexcept { return its; }
+ const auto& get_index() const noexcept { return index_ref; }
};
// Splits a mesh into multiple meshes when possible.
@@ -97,20 +116,19 @@ void its_split(const Its &m, OutputIt out_it)
const indexed_triangle_set &its = ItsWithNeighborsIndex_::get_its(m);
- std::vector visited(its.indices.size(), false);
-
struct VertexConv {
size_t part_id = std::numeric_limits::max();
size_t vertex_image;
};
std::vector vidx_conv(its.vertices.size());
- const auto& neighbor_index = ItsWithNeighborsIndex_::get_index(m);
-
+ meshsplit_detail::NeighborVisitor visitor(its, meshsplit_detail::ItsWithNeighborsIndex_::get_index(m));
+
+ std::vector facets;
for (size_t part_id = 0;; ++part_id) {
- std::vector facets =
- its_find_unvisited_neighbors(its, neighbor_index, visited);
-
+ // Collect all faces of the next patch.
+ facets.clear();
+ visitor.visit([&facets](size_t idx) { facets.emplace_back(idx); return true; });
if (facets.empty())
break;
@@ -150,17 +168,34 @@ std::vector its_split(const Its &its)
return ret;
}
-template bool its_is_splittable(const Its &m)
+template
+bool its_is_splittable(const Its &m)
{
- using namespace meshsplit_detail;
- const indexed_triangle_set &its = ItsWithNeighborsIndex_::get_its(m);
- const auto& neighbor_index = ItsWithNeighborsIndex_::get_index(m);
+ meshsplit_detail::NeighborVisitor visitor(meshsplit_detail::ItsWithNeighborsIndex_::get_its(m), meshsplit_detail::ItsWithNeighborsIndex_::get_index(m));
+ bool has_some = false;
+ bool has_some2 = false;
+ // Traverse the 1st patch fully.
+ visitor.visit([&has_some](size_t idx) { has_some = true; return true; });
+ if (has_some)
+ // Just check whether there is any face of the 2nd patch.
+ visitor.visit([&has_some2](size_t idx) { has_some2 = true; return false; });
+ return has_some && has_some2;
+}
- std::vector visited(its.indices.size(), false);
- its_find_unvisited_neighbors(its, neighbor_index, visited);
- auto faces = its_find_unvisited_neighbors(its, neighbor_index, visited);
-
- return !faces.empty();
+template
+size_t its_number_of_patches(const Its &m)
+{
+ meshsplit_detail::NeighborVisitor visitor(meshsplit_detail::ItsWithNeighborsIndex_::get_its(m), meshsplit_detail::ItsWithNeighborsIndex_::get_index(m));
+ size_t num_patches = 0;
+ for (;;) {
+ bool has_some = false;
+ // Traverse the 1st patch fully.
+ visitor.visit([&has_some](size_t idx) { has_some = true; return true; });
+ if (! has_some)
+ break;
+ ++ num_patches;
+ }
+ return num_patches;
}
template
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index 2844f644c0..b42dfb6a4a 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -475,10 +475,10 @@ bool Model::looks_like_imperial_units() const
void Model::convert_from_imperial_units(bool only_small_volumes)
{
- static constexpr const double in_to_mm = 25.4;
+ static constexpr const float in_to_mm = 25.4f;
for (ModelObject* obj : this->objects)
if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_inches) {
- obj->scale_mesh_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm));
+ obj->scale_mesh_after_creation(in_to_mm);
for (ModelVolume* v : obj->volumes) {
assert(! v->source.is_converted_from_meters);
v->source.is_converted_from_inches = true;
@@ -505,7 +505,7 @@ void Model::convert_from_meters(bool only_small_volumes)
static constexpr const double m_to_mm = 1000;
for (ModelObject* obj : this->objects)
if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_meters) {
- obj->scale_mesh_after_creation(Vec3d(m_to_mm, m_to_mm, m_to_mm));
+ obj->scale_mesh_after_creation(m_to_mm);
for (ModelVolume* v : obj->volumes) {
assert(! v->source.is_converted_from_inches);
v->source.is_converted_from_meters = true;
@@ -1062,11 +1062,11 @@ void ModelObject::mirror(Axis axis)
}
// This method could only be called before the meshes of this ModelVolumes are not shared!
-void ModelObject::scale_mesh_after_creation(const Vec3d &versor)
+void ModelObject::scale_mesh_after_creation(const float scale)
{
for (ModelVolume *v : this->volumes) {
- v->scale_geometry_after_creation(versor);
- v->set_offset(versor.cwiseProduct(v->get_offset()));
+ v->scale_geometry_after_creation(scale);
+ v->set_offset(Vec3d(scale, scale, scale).cwiseProduct(v->get_offset()));
}
this->invalidate_bounding_box();
}
@@ -1077,9 +1077,8 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
ModelObject* new_object = new_clone(*this);
- double koef = conv_type == ConversionType::CONV_FROM_INCH ? 25.4 : conv_type == ConversionType::CONV_TO_INCH ? 0.0393700787 :
- conv_type == ConversionType::CONV_FROM_METER ? 1000 : conv_type == ConversionType::CONV_TO_METER ? 0.001 : 1;
- const Vec3d versor = Vec3d(koef, koef, koef);
+ float koef = conv_type == ConversionType::CONV_FROM_INCH ? 25.4f : conv_type == ConversionType::CONV_TO_INCH ? 0.0393700787f :
+ conv_type == ConversionType::CONV_FROM_METER ? 1000.f : conv_type == ConversionType::CONV_TO_METER ? 0.001f : 1.f;
new_object->set_model(nullptr);
new_object->sla_support_points.clear();
@@ -1092,7 +1091,6 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
for (ModelVolume* volume : volumes) {
if (!volume->mesh().empty()) {
TriangleMesh mesh(volume->mesh());
- mesh.require_shared_vertices();
ModelVolume* vol = new_object->add_volume(mesh);
vol->name = volume->name;
@@ -1118,8 +1116,8 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
if (//vol->source.is_converted_from_inches != from_imperial &&
(volume_idxs.empty() ||
std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end())) {
- vol->scale_geometry_after_creation(versor);
- vol->set_offset(versor.cwiseProduct(volume->get_offset()));
+ vol->scale_geometry_after_creation(koef);
+ vol->set_offset(Vec3d(koef, koef, koef).cwiseProduct(volume->get_offset()));
if (conv_type == ConversionType::CONV_FROM_INCH || conv_type == ConversionType::CONV_TO_INCH)
vol->source.is_converted_from_inches = conv_type == ConversionType::CONV_FROM_INCH;
if (conv_type == ConversionType::CONV_FROM_METER || conv_type == ConversionType::CONV_TO_METER)
@@ -1164,14 +1162,6 @@ size_t ModelObject::parts_count() const
return num;
}
-bool ModelObject::needed_repair() const
-{
- for (const ModelVolume *v : this->volumes)
- if (v->is_model_part() && v->mesh().needed_repair())
- return true;
- return false;
-}
-
ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes)
{
if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower))
@@ -1253,21 +1243,14 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
TriangleMesh upper_mesh, lower_mesh;
{
indexed_triangle_set upper_its, lower_its;
- mesh.require_shared_vertices();
cut_mesh(mesh.its, float(z), &upper_its, &lower_its);
- if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
+ if (attributes.has(ModelObjectCutAttribute::KeepUpper))
upper_mesh = TriangleMesh(upper_its);
- upper_mesh.repair();
- upper_mesh.reset_repair_stats();
- }
- if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
+ if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower_mesh = TriangleMesh(lower_its);
- lower_mesh.repair();
- lower_mesh.reset_repair_stats();
- }
}
- if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper_mesh.facets_count() > 0) {
+ if (attributes.has(ModelObjectCutAttribute::KeepUpper) && ! upper_mesh.empty()) {
ModelVolume* vol = upper->add_volume(upper_mesh);
vol->name = volume->name;
// Don't copy the config's ID.
@@ -1276,7 +1259,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
}
- if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower_mesh.facets_count() > 0) {
+ if (attributes.has(ModelObjectCutAttribute::KeepLower) && ! lower_mesh.empty()) {
ModelVolume* vol = lower->add_volume(lower_mesh);
vol->name = volume->name;
// Don't copy the config's ID.
@@ -1346,24 +1329,22 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
if (volume->type() != ModelVolumeType::MODEL_PART)
continue;
- TriangleMeshPtrs meshptrs = volume->mesh().split();
+ std::vector meshes = volume->mesh().split();
size_t counter = 1;
- for (TriangleMesh* mesh : meshptrs) {
-
+ for (TriangleMesh &mesh : meshes) {
// FIXME: crashes if not satisfied
- if (mesh->facets_count() < 3) continue;
-
- mesh->repair();
+ if (mesh.facets_count() < 3)
+ continue;
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
ModelObject* new_object = m_model->add_object();
- if (meshptrs.size() == 1) {
+ if (meshes.size() == 1) {
new_object->name = volume->name;
// Don't copy the config's ID.
new_object->config.assign_config(this->config.size() > 0 ? this->config : volume->config);
}
else {
- new_object->name = this->name + (meshptrs.size() > 1 ? "_" + std::to_string(counter++) : "");
+ new_object->name = this->name + (meshes.size() > 1 ? "_" + std::to_string(counter++) : "");
// Don't copy the config's ID.
new_object->config.assign_config(this->config);
}
@@ -1372,7 +1353,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
new_object->instances.reserve(this->instances.size());
for (const ModelInstance* model_instance : this->instances)
new_object->add_instance(*model_instance);
- ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh));
+ ModelVolume* new_vol = new_object->add_volume(*volume, std::move(mesh));
for (ModelInstance* model_instance : new_object->instances)
{
@@ -1384,7 +1365,6 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
// reset the source to disable reload from disk
new_vol->source = ModelVolume::Source();
new_objects->emplace_back(new_object);
- delete mesh;
}
}
}
@@ -1402,7 +1382,6 @@ void ModelObject::merge()
for (ModelVolume* volume : volumes)
if (!volume->mesh().empty())
mesh.merge(volume->mesh());
- mesh.repair();
this->clear_volumes();
ModelVolume* vol = this->add_volume(mesh);
@@ -1569,7 +1548,6 @@ void ModelObject::print_info() const
boost::nowide::cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl;
TriangleMesh mesh = this->raw_mesh();
- mesh.check_topology();
BoundingBoxf3 bb = mesh.bounding_box();
Vec3d size = bb.size();
cout << "size_x = " << size(0) << endl;
@@ -1582,19 +1560,18 @@ void ModelObject::print_info() const
cout << "max_y = " << bb.max(1) << endl;
cout << "max_z = " << bb.max(2) << endl;
cout << "number_of_facets = " << mesh.facets_count() << endl;
- cout << "manifold = " << (mesh.is_manifold() ? "yes" : "no") << endl;
+
+ cout << "manifold = " << (mesh.stats().manifold() ? "yes" : "no") << endl;
+ if (! mesh.stats().manifold())
+ cout << "open_edges = " << mesh.stats().open_edges << endl;
- mesh.repair(); // this calculates number_of_parts
- if (mesh.needed_repair()) {
- mesh.repair();
+ if (mesh.stats().repaired()) {
if (mesh.stats().degenerate_facets > 0)
cout << "degenerate_facets = " << mesh.stats().degenerate_facets << endl;
if (mesh.stats().edges_fixed > 0)
cout << "edges_fixed = " << mesh.stats().edges_fixed << endl;
if (mesh.stats().facets_removed > 0)
cout << "facets_removed = " << mesh.stats().facets_removed << endl;
- if (mesh.stats().facets_added > 0)
- cout << "facets_added = " << mesh.stats().facets_added << endl;
if (mesh.stats().facets_reversed > 0)
cout << "facets_reversed = " << mesh.stats().facets_reversed << endl;
if (mesh.stats().backwards_edges > 0)
@@ -1624,24 +1601,24 @@ std::string ModelObject::get_export_filename() const
return ret;
}
-stl_stats ModelObject::get_object_stl_stats() const
+TriangleMeshStats ModelObject::get_object_stl_stats() const
{
if (this->volumes.size() == 1)
return this->volumes[0]->mesh().stats();
- stl_stats full_stats;
+ TriangleMeshStats full_stats;
full_stats.volume = 0.f;
// fill full_stats from all objet's meshes
for (ModelVolume* volume : this->volumes)
{
- const stl_stats& stats = volume->mesh().stats();
+ const TriangleMeshStats& stats = volume->mesh().stats();
// initialize full_stats (for repaired errors)
+ full_stats.open_edges += stats.open_edges;
full_stats.degenerate_facets += stats.degenerate_facets;
full_stats.edges_fixed += stats.edges_fixed;
full_stats.facets_removed += stats.facets_removed;
- full_stats.facets_added += stats.facets_added;
full_stats.facets_reversed += stats.facets_reversed;
full_stats.backwards_edges += stats.backwards_edges;
@@ -1660,10 +1637,10 @@ int ModelObject::get_mesh_errors_count(const int vol_idx /*= -1*/) const
if (vol_idx >= 0)
return this->volumes[vol_idx]->get_mesh_errors_count();
- const stl_stats& stats = get_object_stl_stats();
+ const TriangleMeshStats& stats = get_object_stl_stats();
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
- stats.facets_added + stats.facets_reversed + stats.backwards_edges;
+ stats.facets_reversed + stats.backwards_edges;
}
void ModelVolume::set_material_id(t_model_material_id material_id)
@@ -1727,14 +1704,15 @@ void ModelVolume::center_geometry_after_creation(bool update_source_offset)
void ModelVolume::calculate_convex_hull()
{
m_convex_hull = std::make_shared(this->mesh().convex_hull_3d());
+ assert(m_convex_hull.get());
}
int ModelVolume::get_mesh_errors_count() const
{
- const stl_stats &stats = this->mesh().stats();
+ const TriangleMeshStats &stats = this->mesh().stats();
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
- stats.facets_added + stats.facets_reversed + stats.backwards_edges;
+ stats.facets_reversed + stats.backwards_edges;
}
const TriangleMesh& ModelVolume::get_convex_hull() const
@@ -1782,11 +1760,9 @@ std::string ModelVolume::type_to_string(const ModelVolumeType t)
// This is useful to assign different materials to different volumes of an object.
size_t ModelVolume::split(unsigned int max_extruders)
{
- TriangleMeshPtrs meshptrs = this->mesh().split();
- if (meshptrs.size() <= 1) {
- delete meshptrs.front();
+ std::vector meshes = this->mesh().split();
+ if (meshes.size() <= 1)
return 1;
- }
size_t idx = 0;
size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin();
@@ -1795,15 +1771,14 @@ size_t ModelVolume::split(unsigned int max_extruders)
unsigned int extruder_counter = 0;
Vec3d offset = this->get_offset();
- for (TriangleMesh *mesh : meshptrs) {
- mesh->repair();
- if (mesh->empty())
+ for (TriangleMesh &mesh : meshes) {
+ if (mesh.empty())
// Repair may have removed unconnected triangles, thus emptying the mesh.
continue;
if (idx == 0)
{
- this->set_mesh(std::move(*mesh));
+ this->set_mesh(std::move(mesh));
this->calculate_convex_hull();
// Assign a new unique ID, so that a new GLVolume will be generated.
this->set_new_unique_id();
@@ -1811,7 +1786,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
this->source = ModelVolume::Source();
}
else
- this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(*mesh)));
+ this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(mesh)));
this->object->volumes[ivolume]->set_offset(Vec3d::Zero());
this->object->volumes[ivolume]->center_geometry_after_creation();
@@ -1819,7 +1794,6 @@ size_t ModelVolume::split(unsigned int max_extruders)
this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1);
this->object->volumes[ivolume]->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter));
this->object->volumes[ivolume]->m_is_splittable = 0;
- delete mesh;
++ idx;
}
@@ -1888,7 +1862,7 @@ void ModelVolume::mirror(Axis axis)
}
// This method could only be called before the meshes of this ModelVolumes are not shared!
-void ModelVolume::scale_geometry_after_creation(const Vec3d& versor)
+void ModelVolume::scale_geometry_after_creation(const Vec3f& versor)
{
const_cast(m_mesh.get())->scale(versor);
const_cast(m_convex_hull.get())->scale(versor);
@@ -1921,8 +1895,7 @@ void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_hand
void ModelVolume::convert_from_imperial_units()
{
assert(! this->source.is_converted_from_meters);
- double in_to_mm = 25.4;
- this->scale_geometry_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm));
+ this->scale_geometry_after_creation(25.4f);
this->set_offset(Vec3d(0, 0, 0));
this->source.is_converted_from_inches = true;
}
@@ -1930,8 +1903,7 @@ void ModelVolume::convert_from_imperial_units()
void ModelVolume::convert_from_meters()
{
assert(! this->source.is_converted_from_inches);
- double m_to_mm = 1000;
- this->scale_geometry_after_creation(Vec3d(m_to_mm, m_to_mm, m_to_mm));
+ this->scale_geometry_after_creation(1000.f);
this->set_offset(Vec3d(0, 0, 0));
this->source.is_converted_from_meters = true;
}
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index 07274d3521..ea1d0ed175 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -346,13 +346,12 @@ public:
void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
- void scale_mesh_after_creation(const Vec3d& versor);
+ void scale_mesh_after_creation(const float scale);
void convert_units(ModelObjectPtrs&new_objects, ConversionType conv_type, std::vector volume_idxs);
size_t materials_count() const;
size_t facets_count() const;
size_t parts_count() const;
- bool needed_repair() const;
ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes);
void split(ModelObjectPtrs* new_objects);
void merge();
@@ -376,7 +375,7 @@ public:
std::string get_export_filename() const;
// Get full stl statistics for all object's meshes
- stl_stats get_object_stl_stats() const;
+ TriangleMeshStats get_object_stl_stats() const;
// Get count of errors in the mesh( or all object's meshes, if volume index isn't defined)
int get_mesh_errors_count(const int vol_idx = -1) const;
@@ -620,6 +619,8 @@ public:
const TriangleMesh& mesh() const { return *m_mesh.get(); }
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared(mesh); }
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared(std::move(mesh)); }
+ void set_mesh(const indexed_triangle_set &mesh) { m_mesh = std::make_shared(mesh); }
+ void set_mesh(indexed_triangle_set &&mesh) { m_mesh = std::make_shared(std::move(mesh)); }
void set_mesh(std::shared_ptr &mesh) { m_mesh = mesh; }
void set_mesh(std::unique_ptr &&mesh) { m_mesh = std::move(mesh); }
void reset_mesh() { m_mesh = std::make_shared(); }
@@ -670,7 +671,8 @@ public:
void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
- void scale_geometry_after_creation(const Vec3d& versor);
+ void scale_geometry_after_creation(const Vec3f &versor);
+ void scale_geometry_after_creation(const float scale) { this->scale_geometry_after_creation(Vec3f(scale, scale, scale)); }
// Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box.
// Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared!
diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp
index 20726270d8..21eb48c2ee 100644
--- a/src/libslic3r/Point.hpp
+++ b/src/libslic3r/Point.hpp
@@ -418,7 +418,7 @@ template>
inline constexpr Tout unscaled(const Tin &v) noexcept
{
- return Tout(v * Tout(SCALING_FACTOR));
+ return Tout(v) * Tout(SCALING_FACTOR);
}
// Unscaling for Eigen vectors. Input base type can be arithmetic, output base
@@ -432,7 +432,7 @@ template
unscaled(const Eigen::Matrix &v) noexcept
{
- return v.template cast() * SCALING_FACTOR;
+ return v.template cast() * Tout(SCALING_FACTOR);
}
// Align a coordinate to a grid. The coordinate may be negative,
diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp
index 10960c5356..da26f905cb 100644
--- a/src/libslic3r/PrintApply.cpp
+++ b/src/libslic3r/PrintApply.cpp
@@ -1300,8 +1300,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
num_extruders,
painting_extruders,
*print_object_regions,
- [&print_object, &update_apply_status](const PrintRegionConfig &old_config, const PrintRegionConfig &new_config, const t_config_option_keys &diff_keys) {
- update_apply_status(print_object.invalidate_state_by_config_options(old_config, new_config, diff_keys));
+ [&print_object, it_print_object, it_print_object_end, &update_apply_status](const PrintRegionConfig &old_config, const PrintRegionConfig &new_config, const t_config_option_keys &diff_keys) {
+ for (auto it = it_print_object; it != it_print_object_end; ++it)
+ if ((*it)->m_shared_regions != nullptr)
+ update_apply_status((*it)->invalidate_state_by_config_options(old_config, new_config, diff_keys));
})) {
// Regions are valid, just keep them.
} else {
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index d9a4f2670a..74ef27bd74 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -1447,7 +1447,7 @@ void PrintObject::bridge_over_infill()
Polygons to_bridge_pp = internal_solid;
// iterate through lower layers spanned by bridge_flow
- double bottom_z = layer->print_z - bridge_flow.height();
+ double bottom_z = layer->print_z - bridge_flow.height() - EPSILON;
for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) {
const Layer* lower_layer = m_layers[i];
diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp
index 55dae94309..9cac7f63bc 100644
--- a/src/libslic3r/SLA/Hollowing.cpp
+++ b/src/libslic3r/SLA/Hollowing.cpp
@@ -286,8 +286,6 @@ void cut_drainholes(std::vector & obj_slices,
if (mesh.empty()) return;
- mesh.require_shared_vertices();
-
std::vector hole_slices = slice_mesh_ex(mesh.its, slicegrid, closing_radius, thr);
if (obj_slices.size() != hole_slices.size())
@@ -316,7 +314,6 @@ void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags)
remove_inside_triangles(mesh, interior);
mesh.merge(TriangleMesh{interior.mesh});
- mesh.require_shared_vertices();
}
// Get the distance of p to the interior's zero iso_surface. Interior should
@@ -557,8 +554,7 @@ void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior,
new_faces = {};
mesh = TriangleMesh{mesh.its};
- mesh.repaired = true;
- mesh.require_shared_vertices();
+ //FIXME do we want to repair the mesh? Are there duplicate vertices or flipped triangles?
}
}} // namespace Slic3r::sla
diff --git a/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp b/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp
index 20804193e2..3ad7d62b1e 100644
--- a/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp
+++ b/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp
@@ -33,7 +33,6 @@ inline void reproject_points_and_holes(ModelObject *object)
if (!object || (!has_holes && !has_sppoints)) return;
TriangleMesh rmsh = object->raw_mesh();
- rmsh.require_shared_vertices();
IndexedMesh emesh{rmsh};
if (has_sppoints)
diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp
index 5486741f26..196646dc9e 100644
--- a/src/libslic3r/SLA/Rotfinder.cpp
+++ b/src/libslic3r/SLA/Rotfinder.cpp
@@ -205,7 +205,6 @@ inline bool is_on_floor(const SLAPrintObjectConfig &cfg)
std::vector get_chull_rotations(const TriangleMesh &mesh, size_t max_count)
{
TriangleMesh chull = mesh.convex_hull_3d();
- chull.require_shared_vertices();
double chull2d_area = chull.convex_hull().area();
double area_threshold = chull2d_area / (scaled(1e3) * scaled(1.));
@@ -299,7 +298,6 @@ struct RotfinderBoilerplate {
static TriangleMesh get_mesh_to_rotate(const ModelObject &mo)
{
TriangleMesh mesh = mo.raw_mesh();
- mesh.require_shared_vertices();
ModelInstance *mi = mo.instances[0];
auto rotation = Vec3d::Zero();
@@ -437,7 +435,6 @@ Vec2d find_min_z_height_rotation(const ModelObject &mo,
RotfinderBoilerplate<1000> bp{mo, params};
TriangleMesh chull = bp.mesh.convex_hull_3d();
- chull.require_shared_vertices();
auto inputs = reserve_vector(chull.its.indices.size());
auto rotcmp = [](const XYRotation &r1, const XYRotation &r2) {
double xdiff = r1[X] - r2[X], ydiff = r1[Y] - r2[Y];
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 61ff908d3b..a09f5ea98d 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -896,7 +896,6 @@ SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object)
obj = m_model_object->raw_mesh();
if (!obj.empty()) {
obj.transform(m_trafo);
- obj.require_shared_vertices();
}
})
{}
diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp
index 0cd80f20b7..e11926c7ea 100644
--- a/src/libslic3r/SLAPrint.hpp
+++ b/src/libslic3r/SLAPrint.hpp
@@ -323,7 +323,6 @@ private:
{
support_tree_ptr = sla::SupportTree::create(*this, ctl);
tree_mesh = TriangleMesh{support_tree_ptr->retrieve_mesh(sla::MeshType::Support)};
- tree_mesh.require_shared_vertices();
return support_tree_ptr;
}
diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp
index 4b377d9f11..adec5735a8 100644
--- a/src/libslic3r/SLAPrintSteps.cpp
+++ b/src/libslic3r/SLAPrintSteps.cpp
@@ -526,7 +526,6 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
}
auto thr = [this]() { m_print->throw_if_canceled(); };
auto &slice_grid = po.m_model_height_levels;
- assert(mesh.has_shared_vertices());
po.m_model_slices = slice_mesh_ex(mesh.its, slice_grid, params, thr);
sla::Interior *interior = po.m_hollowing_data ?
diff --git a/src/libslic3r/SimplifyMesh.hpp b/src/libslic3r/SimplifyMesh.hpp
index fb3e73d049..23eb343d1c 100644
--- a/src/libslic3r/SimplifyMesh.hpp
+++ b/src/libslic3r/SimplifyMesh.hpp
@@ -14,10 +14,8 @@ void simplify_mesh(indexed_triangle_set &);
template void simplify_mesh(TriangleMesh &m, Args &&...a)
{
- m.require_shared_vertices();
simplify_mesh(m.its, std::forward(a)...);
- m = TriangleMesh{m.its};
- m.require_shared_vertices();
+ m = TriangleMesh{ std::move(m.its) };
}
} // namespace Slic3r
diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp
index f727d334d9..2bcf99c57e 100644
--- a/src/libslic3r/SupportMaterial.cpp
+++ b/src/libslic3r/SupportMaterial.cpp
@@ -721,9 +721,9 @@ public:
#ifdef SUPPORT_USE_AGG_RASTERIZER
m_bbox = bbox;
// Oversample the grid to avoid leaking of supports through or around the object walls.
- int oversampling = std::min(8, int(scale_(m_support_spacing) / (scale_(params.extrusion_width) + 100)));
- m_pixel_size = scale_(m_support_spacing / oversampling);
- assert(scale_(params.extrusion_width) + 20 < m_pixel_size);
+ int extrusion_width_scaled = scale_(params.extrusion_width);
+ int oversampling = std::clamp(int(scale_(m_support_spacing) / (extrusion_width_scaled + 100)), 1, 8);
+ m_pixel_size = std::max(extrusion_width_scaled + 21, scale_(m_support_spacing / oversampling));
// Add one empty column / row boundaries.
m_bbox.offset(m_pixel_size);
// Grid size fitting the support polygons plus one pixel boundary around the polygons.
@@ -2600,8 +2600,6 @@ void PrintObjectSupportMaterial::generate_base_layers(
// No top contacts -> no intermediate layers will be produced.
return;
- // coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
-
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_base_layers() in parallel - start";
tbb::parallel_for(
tbb::blocked_range(0, intermediate_layers.size()),
@@ -2696,6 +2694,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
layer_intermediate.layer_type = sltBase;
#if 0
+ // coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
// Fillet the base polygons and trim them again with the top, interface and contact layers.
$base->{$i} = diff(
offset2(
@@ -3784,7 +3783,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// Prepare fillers.
SupportMaterialPattern support_pattern = m_object_config->support_material_pattern;
bool with_sheath = m_object_config->support_material_with_sheath;
- InfillPattern infill_pattern = (support_pattern == smpHoneycomb ? ipHoneycomb : ipSupportBase);
+ InfillPattern infill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : (support_density < 1.05 ? ipRectilinear : ipSupportBase);
std::vector angles;
angles.push_back(base_angle);
diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 2da90ce9eb..454e452748 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -41,8 +41,8 @@
//====================
#define ENABLE_2_4_0_ALPHA1 1
-// Enable the fix for exporting and importing to/from 3mf file of mirrored volumes
-#define ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT (1 && ENABLE_2_4_0_ALPHA1)
+// Enable implementation of retract acceleration in gcode processor
+#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA1)
// Enable rendering seams (and other options) in preview using models
#define ENABLE_SEAMS_USING_MODELS (1 && ENABLE_2_4_0_ALPHA1)
// Enable save and save as commands to be enabled also when the plater is empty and allow to load empty projects
@@ -60,6 +60,8 @@
#define ENABLE_FIX_PREVIEW_OPTIONS_Z (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_2_4_0_ALPHA2)
// Enable replacing a missing file during reload from disk command
#define ENABLE_RELOAD_FROM_DISK_REPLACE_FILE (1 && ENABLE_2_4_0_ALPHA2)
+// Enable fixing the synchronization of seams with the horizontal slider in preview
+#define ENABLE_FIX_SEAMS_SYNCH (1 && ENABLE_2_4_0_ALPHA2)
// Enable changes in preview layout
#define ENABLE_PREVIEW_LAYOUT (1 && ENABLE_2_4_0_ALPHA2)
diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp
index d289fca141..73ac2eade5 100644
--- a/src/libslic3r/TriangleMesh.cpp
+++ b/src/libslic3r/TriangleMesh.cpp
@@ -7,6 +7,7 @@
#include "Point.hpp"
#include "Execution/ExecutionTBB.hpp"
#include "Execution/ExecutionSeq.hpp"
+#include "Utils.hpp"
#include
#include
@@ -21,6 +22,7 @@
#include
#include
+#include
#include
#include
@@ -29,74 +31,51 @@
namespace Slic3r {
-TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector &facets) : repaired(false)
+static void update_bounding_box(const indexed_triangle_set &its, TriangleMeshStats &out)
{
- stl_file &stl = this->stl;
- stl.stats.type = inmemory;
-
- // count facets and allocate memory
- stl.stats.number_of_facets = (uint32_t)facets.size();
- stl.stats.original_num_facets = stl.stats.number_of_facets;
- stl_allocate(&stl);
-
- for (uint32_t i = 0; i < stl.stats.number_of_facets; ++ i) {
- stl_facet facet;
- facet.vertex[0] = points[facets[i](0)].cast();
- facet.vertex[1] = points[facets[i](1)].cast();
- facet.vertex[2] = points[facets[i](2)].cast();
- facet.extra[0] = 0;
- facet.extra[1] = 0;
-
- stl_normal normal;
- stl_calculate_normal(normal, &facet);
- stl_normalize_vector(normal);
- facet.normal = normal;
-
- stl.facet_start[i] = facet;
- }
- stl_get_size(&stl);
+ BoundingBoxf3 bbox = Slic3r::bounding_box(its);
+ out.min = bbox.min.cast();
+ out.max = bbox.max.cast();
+ out.size = out.max - out.min;
}
-TriangleMesh::TriangleMesh(const indexed_triangle_set &M) : repaired(false)
+static void fill_initial_stats(const indexed_triangle_set &its, TriangleMeshStats &out)
{
- stl.stats.type = inmemory;
-
- // count facets and allocate memory
- stl.stats.number_of_facets = uint32_t(M.indices.size());
- stl.stats.original_num_facets = int(stl.stats.number_of_facets);
- stl_allocate(&stl);
-
- for (uint32_t i = 0; i < stl.stats.number_of_facets; ++ i) {
- stl_facet facet;
- facet.vertex[0] = M.vertices[size_t(M.indices[i](0))];
- facet.vertex[1] = M.vertices[size_t(M.indices[i](1))];
- facet.vertex[2] = M.vertices[size_t(M.indices[i](2))];
- facet.extra[0] = 0;
- facet.extra[1] = 0;
-
- stl_normal normal;
- stl_calculate_normal(normal, &facet);
- stl_normalize_vector(normal);
- facet.normal = normal;
-
- stl.facet_start[i] = facet;
- }
-
- stl_get_size(&stl);
+ out.number_of_facets = its.indices.size();
+ out.volume = its_volume(its);
+ update_bounding_box(its, out);
+
+ const std::vector face_neighbors = its_face_neighbors(its);
+ out.number_of_parts = its_number_of_patches(its, face_neighbors);
+ out.open_edges = its_num_open_edges(face_neighbors);
+}
+
+TriangleMesh::TriangleMesh(const std::vector &vertices, const std::vector &faces) : its { faces, vertices }
+{
+ fill_initial_stats(this->its, m_stats);
+}
+
+TriangleMesh::TriangleMesh(std::vector &&vertices, const std::vector &&faces) : its { std::move(faces), std::move(vertices) }
+{
+ fill_initial_stats(this->its, m_stats);
+}
+
+TriangleMesh::TriangleMesh(const indexed_triangle_set &its) : its(its)
+{
+ fill_initial_stats(this->its, m_stats);
+}
+
+TriangleMesh::TriangleMesh(indexed_triangle_set &&its) : its(std::move(its))
+{
+ fill_initial_stats(this->its, m_stats);
}
// #define SLIC3R_TRACE_REPAIR
-void TriangleMesh::repair(bool update_shared_vertices)
+static void trianglemesh_repair_on_import(stl_file &stl)
{
- if (this->repaired) {
- if (update_shared_vertices)
- this->require_shared_vertices();
- return;
- }
-
// admesh fails when repairing empty meshes
- if (this->stl.stats.number_of_facets == 0)
+ if (stl.stats.number_of_facets == 0)
return;
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() started";
@@ -105,9 +84,9 @@ void TriangleMesh::repair(bool update_shared_vertices)
#ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact";
#endif /* SLIC3R_TRACE_REPAIR */
- assert(stl_validate(&this->stl));
+ assert(stl_validate(&stl));
stl_check_facets_exact(&stl);
- assert(stl_validate(&this->stl));
+ assert(stl_validate(&stl));
stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge);
stl.stats.facets_w_2_bad_edge = (stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge);
stl.stats.facets_w_3_bad_edge = (stl.stats.number_of_facets - stl.stats.connected_facets_1_edge);
@@ -117,9 +96,11 @@ void TriangleMesh::repair(bool update_shared_vertices)
float tolerance = (float)stl.stats.shortest_edge;
float increment = (float)stl.stats.bounding_diameter / 10000.0f;
int iterations = 2;
- if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
- for (int i = 0; i < iterations; i++) {
- if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
+ if (stl.stats.connected_facets_3_edge < int(stl.stats.number_of_facets)) {
+ // Not a manifold, some triangles have unconnected edges.
+ for (int i = 0; i < iterations; ++ i) {
+ if (stl.stats.connected_facets_3_edge < int(stl.stats.number_of_facets)) {
+ // Still not a manifold, some triangles have unconnected edges.
//printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations);
#ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_nearby";
@@ -133,7 +114,7 @@ void TriangleMesh::repair(bool update_shared_vertices)
}
}
}
- assert(stl_validate(&this->stl));
+ assert(stl_validate(&stl));
// remove_unconnected
if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
@@ -141,7 +122,7 @@ void TriangleMesh::repair(bool update_shared_vertices)
BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets";
#endif /* SLIC3R_TRACE_REPAIR */
stl_remove_unconnected_facets(&stl);
- assert(stl_validate(&this->stl));
+ assert(stl_validate(&stl));
}
// fill_holes
@@ -162,97 +143,82 @@ void TriangleMesh::repair(bool update_shared_vertices)
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_directions";
#endif /* SLIC3R_TRACE_REPAIR */
stl_fix_normal_directions(&stl);
- assert(stl_validate(&this->stl));
+ assert(stl_validate(&stl));
// normal_values
#ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_values";
#endif /* SLIC3R_TRACE_REPAIR */
stl_fix_normal_values(&stl);
- assert(stl_validate(&this->stl));
+ assert(stl_validate(&stl));
// always calculate the volume and reverse all normals if volume is negative
#ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_calculate_volume";
#endif /* SLIC3R_TRACE_REPAIR */
+ // If the volume is negative, all the facets are flipped and added to stats.facets_reversed.
stl_calculate_volume(&stl);
- assert(stl_validate(&this->stl));
+ assert(stl_validate(&stl));
// neighbors
#ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_verify_neighbors";
#endif /* SLIC3R_TRACE_REPAIR */
stl_verify_neighbors(&stl);
- assert(stl_validate(&this->stl));
-
- this->repaired = true;
+ assert(stl_validate(&stl));
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
- if (auto nr_degenerated = this->stl.stats.degenerate_facets; this->facets_count() > 0 && nr_degenerated > 0)
- stl_check_facets_exact(&this->stl);
+ if (auto nr_degenerated = stl.stats.degenerate_facets; stl.stats.number_of_facets > 0 && nr_degenerated > 0)
+ stl_check_facets_exact(&stl);
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
+}
- // This call should be quite cheap, a lot of code requires the indexed_triangle_set data structure,
- // and it is risky to generate such a structure once the meshes are shared. Do it now.
- this->its.clear();
- if (update_shared_vertices)
- this->require_shared_vertices();
+bool TriangleMesh::ReadSTLFile(const char* input_file, bool repair)
+{
+ stl_file stl;
+ if (! stl_open(&stl, input_file))
+ return false;
+ if (repair)
+ trianglemesh_repair_on_import(stl);
+
+ m_stats.number_of_facets = stl.stats.number_of_facets;
+ m_stats.min = stl.stats.min;
+ m_stats.max = stl.stats.max;
+ m_stats.size = stl.stats.size;
+ m_stats.volume = stl.stats.volume;
+
+ auto facets_w_1_bad_edge = stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge;
+ auto facets_w_2_bad_edge = stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_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.edges_fixed = stl.stats.edges_fixed;
+ m_stats.degenerate_facets = stl.stats.degenerate_facets;
+ m_stats.facets_removed = stl.stats.facets_removed;
+ m_stats.facets_reversed = stl.stats.facets_reversed;
+ m_stats.backwards_edges = stl.stats.backwards_edges;
+ m_stats.number_of_parts = stl.stats.number_of_parts;
+
+ stl_generate_shared_vertices(&stl, this->its);
+ return true;
+}
+
+bool TriangleMesh::write_ascii(const char* output_file)
+{
+ return its_write_stl_ascii(output_file, "", this->its);
+}
+
+bool TriangleMesh::write_binary(const char* output_file)
+{
+ return its_write_stl_binary(output_file, "", this->its);
}
float TriangleMesh::volume()
{
- if (this->stl.stats.volume == -1)
- stl_calculate_volume(&this->stl);
- return this->stl.stats.volume;
-}
-
-void TriangleMesh::check_topology()
-{
- // checking exact
- stl_check_facets_exact(&stl);
- stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge);
- stl.stats.facets_w_2_bad_edge = (stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge);
- stl.stats.facets_w_3_bad_edge = (stl.stats.number_of_facets - stl.stats.connected_facets_1_edge);
-
- // checking nearby
- //int last_edges_fixed = 0;
- float tolerance = stl.stats.shortest_edge;
- float increment = stl.stats.bounding_diameter / 10000.0;
- int iterations = 2;
- if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
- for (int i = 0; i < iterations; i++) {
- if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
- //printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations);
- stl_check_facets_nearby(&stl, tolerance);
- //printf(" Fixed %d edges.\n", stl.stats.edges_fixed - last_edges_fixed);
- //last_edges_fixed = stl.stats.edges_fixed;
- tolerance += increment;
- } else {
- break;
- }
- }
- }
-}
-
-void TriangleMesh::reset_repair_stats() {
- this->stl.stats.degenerate_facets = 0;
- this->stl.stats.edges_fixed = 0;
- this->stl.stats.facets_removed = 0;
- this->stl.stats.facets_added = 0;
- this->stl.stats.facets_reversed = 0;
- this->stl.stats.backwards_edges = 0;
- this->stl.stats.normals_fixed = 0;
-}
-
-bool TriangleMesh::needed_repair() const
-{
- return this->stl.stats.degenerate_facets > 0
- || this->stl.stats.edges_fixed > 0
- || this->stl.stats.facets_removed > 0
- || this->stl.stats.facets_added > 0
- || this->stl.stats.facets_reversed > 0
- || this->stl.stats.backwards_edges > 0;
+ if (m_stats.volume == -1)
+ m_stats.volume = its_volume(this->its);
+ return m_stats.volume;
}
void TriangleMesh::WriteOBJFile(const char* output_file) const
@@ -262,133 +228,138 @@ void TriangleMesh::WriteOBJFile(const char* output_file) const
void TriangleMesh::scale(float factor)
{
- stl_scale(&(this->stl), factor);
- for (stl_vertex& v : this->its.vertices)
- v *= factor;
+ this->scale(Vec3f(factor, factor, factor));
}
-void TriangleMesh::scale(const Vec3d &versor)
+void TriangleMesh::scale(const Vec3f &versor)
{
- stl_scale_versor(&this->stl, versor.cast());
- for (stl_vertex& v : this->its.vertices) {
- v.x() *= versor.x();
- v.y() *= versor.y();
- v.z() *= versor.z();
+ // Scale extents.
+ auto s = versor.array();
+ m_stats.min.array() *= s;
+ m_stats.max.array() *= s;
+ // Scale size.
+ m_stats.size.array() *= s;
+ // Scale volume.
+ if (m_stats.volume > 0.0)
+ m_stats.volume *= s(0) * s(1) * s(2);
+ if (versor.x() == versor.y() && versor.x() == versor.z()) {
+ float s = versor.x();
+ for (stl_vertex &v : this->its.vertices)
+ v *= s;
+ } else {
+ for (stl_vertex &v : this->its.vertices) {
+ v.x() *= versor.x();
+ v.y() *= versor.y();
+ v.z() *= versor.z();
+ }
+ }
+}
+
+void TriangleMesh::translate(const Vec3f &displacement)
+{
+ if (displacement.x() != 0.f || displacement.y() != 0.f || displacement.z() != 0.f) {
+ for (stl_vertex& v : this->its.vertices)
+ v += displacement;
+ m_stats.min += displacement;
+ m_stats.max += displacement;
}
}
void TriangleMesh::translate(float x, float y, float z)
{
- if (x == 0.f && y == 0.f && z == 0.f)
- return;
- stl_translate_relative(&(this->stl), x, y, z);
- stl_vertex shift(x, y, z);
- for (stl_vertex& v : this->its.vertices)
- v += shift;
-}
-
-void TriangleMesh::translate(const Vec3f &displacement)
-{
- translate(displacement(0), displacement(1), displacement(2));
+ this->translate(Vec3f(x, y, z));
}
void TriangleMesh::rotate(float angle, const Axis &axis)
{
- if (angle == 0.f)
- return;
-
- // admesh uses degrees
- angle = Slic3r::Geometry::rad2deg(angle);
-
- if (axis == X) {
- stl_rotate_x(&this->stl, angle);
- its_rotate_x(this->its, angle);
- } else if (axis == Y) {
- stl_rotate_y(&this->stl, angle);
- its_rotate_y(this->its, angle);
- } else if (axis == Z) {
- stl_rotate_z(&this->stl, angle);
- its_rotate_z(this->its, angle);
+ if (angle != 0.f) {
+ angle = Slic3r::Geometry::rad2deg(angle);
+ switch (axis) {
+ case X: its_rotate_x(this->its, angle); break;
+ case Y: its_rotate_y(this->its, angle); break;
+ case Z: its_rotate_z(this->its, angle); break;
+ default: assert(false); return;
+ }
+ update_bounding_box(this->its, this->m_stats);
}
}
void TriangleMesh::rotate(float angle, const Vec3d& axis)
{
- if (angle == 0.f)
- return;
-
- Vec3d axis_norm = axis.normalized();
- Transform3d m = Transform3d::Identity();
- m.rotate(Eigen::AngleAxisd(angle, axis_norm));
- stl_transform(&stl, m);
- its_transform(its, m);
+ if (angle != 0.f) {
+ Vec3d axis_norm = axis.normalized();
+ Transform3d m = Transform3d::Identity();
+ m.rotate(Eigen::AngleAxisd(angle, axis_norm));
+ its_transform(its, m);
+ update_bounding_box(this->its, this->m_stats);
+ }
}
-void TriangleMesh::mirror(const Axis &axis)
+void TriangleMesh::mirror(const Axis axis)
{
- if (axis == X) {
- stl_mirror_yz(&this->stl);
+ switch (axis) {
+ case X:
+ for (stl_vertex &v : its.vertices)
+ v.x() *= -1.f;
+ break;
+ case Y:
+ for (stl_vertex& v : this->its.vertices)
+ v.y() *= -1.0;
+ break;
+ case Z:
for (stl_vertex &v : this->its.vertices)
- v(0) *= -1.0;
- } else if (axis == Y) {
- stl_mirror_xz(&this->stl);
- for (stl_vertex &v : this->its.vertices)
- v(1) *= -1.0;
- } else if (axis == Z) {
- stl_mirror_xy(&this->stl);
- for (stl_vertex &v : this->its.vertices)
- v(2) *= -1.0;
- }
+ v.z() *= -1.0;
+ break;
+ default:
+ assert(false);
+ return;
+ };
+ its_flip_triangles(this->its);
+ int iaxis = int(axis);
+ std::swap(m_stats.min[iaxis], m_stats.max[iaxis]);
+ m_stats.min[iaxis] *= -1.0;
+ m_stats.max[iaxis] *= -1.0;
}
void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed)
{
- stl_transform(&stl, t);
its_transform(its, t);
- if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.) {
- // Left handed transformation is being applied. It is a good idea to flip the faces and their normals.
- // As for the assert: the repair function would fix the normals, reversing would
- // break them again. The caller should provide a mesh that does not need repair.
- // The repair call is left here so things don't break more than they were.
- assert(this->repaired);
- this->repair(false);
- stl_reverse_all_facets(&stl);
- this->its.clear();
- this->require_shared_vertices();
- }
+ if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.)
+ its_flip_triangles(its);
+ else
+ m_stats.volume = - m_stats.volume;
+ update_bounding_box(this->its, this->m_stats);
}
void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed)
{
- stl_transform(&stl, m);
its_transform(its, m);
- if (fix_left_handed && m.determinant() < 0.) {
- // See comments in function above.
- assert(this->repaired);
- this->repair(false);
- stl_reverse_all_facets(&stl);
- this->its.clear();
- this->require_shared_vertices();
- }
+ if (fix_left_handed && m.determinant() < 0.)
+ its_flip_triangles(its);
+ else
+ m_stats.volume = - m_stats.volume;
+ update_bounding_box(this->its, this->m_stats);
+}
+
+void TriangleMesh::flip_triangles()
+{
+ its_flip_triangles(its);
+ m_stats.volume = - m_stats.volume;
}
void TriangleMesh::align_to_origin()
{
- this->translate(
- - this->stl.stats.min(0),
- - this->stl.stats.min(1),
- - this->stl.stats.min(2));
+ this->translate(- m_stats.min(0), - m_stats.min(1), - m_stats.min(2));
}
void TriangleMesh::rotate(double angle, Point* center)
{
- if (angle == 0.)
- return;
- Vec2f c = center->cast();
- this->translate(-c(0), -c(1), 0);
- stl_rotate_z(&this->stl, (float)angle);
- its_rotate_z(this->its, (float)angle);
- this->translate(c(0), c(1), 0);
+ if (angle != 0.) {
+ Vec2f c = center->cast();
+ this->translate(-c(0), -c(1), 0);
+ its_rotate_z(this->its, (float)angle);
+ this->translate(c(0), c(1), 0);
+ }
}
/**
@@ -396,145 +367,36 @@ void TriangleMesh::rotate(double angle, Point* center)
*/
bool TriangleMesh::is_splittable() const
{
- std::vector visited;
- find_unvisited_neighbors(visited);
-
- // Try finding an unvisited facet. If there are none, the mesh is not splittable.
- auto it = std::find(visited.begin(), visited.end(), false);
- return it != visited.end();
+ return its_is_splittable(this->its);
}
-/**
- * Visit all unvisited neighboring facets that are reachable from the first unvisited facet,
- * and return them.
- *
- * @param facet_visited A reference to a vector of booleans. Contains whether or not a
- * facet with the same index has been visited.
- * @return A deque with all newly visited facets.
- */
-std::deque TriangleMesh::find_unvisited_neighbors(std::vector &facet_visited) const
+std::vector TriangleMesh::split() const
{
- // Make sure we're not operating on a broken mesh.
- if (!this->repaired)
- throw Slic3r::RuntimeError("find_unvisited_neighbors() requires repair()");
+ std::vector itss = its_split(this->its);
+ std::vector out;
+ out.reserve(itss.size());
+ for (indexed_triangle_set &m : itss) {
+ // The TriangleMesh constructor shall fill in the mesh statistics including volume.
+ out.emplace_back(std::move(m));
+ if (TriangleMesh &triangle_mesh = out.back(); triangle_mesh.volume() < 0)
+ // Some source mesh parts may be incorrectly oriented. Correct them.
+ triangle_mesh.flip_triangles();
- // If the visited list is empty, populate it with false for every facet.
- if (facet_visited.empty())
- facet_visited = std::vector(this->stl.stats.number_of_facets, false);
-
- // Find the first unvisited facet.
- std::queue facet_queue;
- std::deque facets;
- auto facet = std::find(facet_visited.begin(), facet_visited.end(), false);
- if (facet != facet_visited.end()) {
- uint32_t idx = uint32_t(facet - facet_visited.begin());
- facet_queue.push(idx);
- facet_visited[idx] = true;
- facets.emplace_back(idx);
}
-
- // Traverse all reachable neighbors and mark them as visited.
- while (! facet_queue.empty()) {
- uint32_t facet_idx = facet_queue.front();
- facet_queue.pop();
- for (int neighbor_idx : this->stl.neighbors_start[facet_idx].neighbor)
- if (neighbor_idx != -1 && ! facet_visited[neighbor_idx]) {
- facet_queue.push(uint32_t(neighbor_idx));
- facet_visited[neighbor_idx] = true;
- facets.emplace_back(uint32_t(neighbor_idx));
- }
- }
-
- return facets;
-}
-
-/**
- * Splits a mesh into multiple meshes when possible.
- *
- * @return A TriangleMeshPtrs with the newly created meshes.
- */
-TriangleMeshPtrs TriangleMesh::split() const
-{
- struct MeshAdder {
- TriangleMeshPtrs &meshes;
- MeshAdder(TriangleMeshPtrs &ptrs): meshes{ptrs} {}
- void operator=(const indexed_triangle_set &its)
- {
- meshes.emplace_back(new TriangleMesh(its));
- }
- };
-
- TriangleMeshPtrs meshes;
- if (has_shared_vertices()) {
- its_split(its, MeshAdder{meshes});
- } else {
- // Loop while we have remaining facets.
- std::vector facet_visited;
- for (;;) {
- std::deque facets = find_unvisited_neighbors(facet_visited);
- if (facets.empty())
- break;
-
- // Create a new mesh for the part that was just split off.
- TriangleMesh* mesh = new TriangleMesh;
- meshes.emplace_back(mesh);
- mesh->stl.stats.type = inmemory;
- mesh->stl.stats.number_of_facets = (uint32_t)facets.size();
- mesh->stl.stats.original_num_facets = mesh->stl.stats.number_of_facets;
- stl_allocate(&mesh->stl);
-
- // Assign the facets to the new mesh.
- bool first = true;
- for (auto facet = facets.begin(); facet != facets.end(); ++ facet) {
- mesh->stl.facet_start[facet - facets.begin()] = this->stl.facet_start[*facet];
- stl_facet_stats(&mesh->stl, this->stl.facet_start[*facet], first);
- }
- }
- }
-
- return meshes;
+ return out;
}
void TriangleMesh::merge(const TriangleMesh &mesh)
{
- // reset stats and metadata
- int number_of_facets = this->stl.stats.number_of_facets;
- this->its.clear();
- this->repaired = false;
-
- // update facet count and allocate more memory
- this->stl.stats.number_of_facets = number_of_facets + mesh.stl.stats.number_of_facets;
- this->stl.stats.original_num_facets = this->stl.stats.number_of_facets;
- stl_reallocate(&this->stl);
-
- // copy facets
- for (uint32_t i = 0; i < mesh.stl.stats.number_of_facets; ++ i)
- this->stl.facet_start[number_of_facets + i] = mesh.stl.facet_start[i];
-
- // update size
- stl_get_size(&this->stl);
+ its_merge(this->its, mesh.its);
+ m_stats = m_stats.merge(mesh.m_stats);
}
// Calculate projection of the mesh into the XY plane, in scaled coordinates.
//FIXME This could be extremely slow! Use it for tiny meshes only!
ExPolygons TriangleMesh::horizontal_projection() const
{
- ClipperLib::Paths paths;
- Polygon p;
- p.points.assign(3, Point());
- auto delta = scaled(0.01);
- std::vector deltas { delta, delta, delta };
- paths.reserve(this->stl.stats.number_of_facets);
- for (const stl_facet &facet : this->stl.facet_start) {
- p.points[0] = Point::new_scale(facet.vertex[0](0), facet.vertex[0](1));
- p.points[1] = Point::new_scale(facet.vertex[1](0), facet.vertex[1](1));
- p.points[2] = Point::new_scale(facet.vertex[2](0), facet.vertex[2](1));
- p.make_counter_clockwise();
- paths.emplace_back(mittered_offset_path_scaled(p.points, deltas, 3.));
- }
-
- // the offset factor was tuned using groovemount.stl
- return ClipperPaths_to_Slic3rExPolygons(paths);
+ return union_ex(project_mesh(this->its, Transform3d::Identity(), []() {}));
}
// 2D convex hull of a 3D mesh projected into the Z=0 plane.
@@ -553,24 +415,16 @@ BoundingBoxf3 TriangleMesh::bounding_box() const
{
BoundingBoxf3 bb;
bb.defined = true;
- bb.min = this->stl.stats.min.cast();
- bb.max = this->stl.stats.max.cast();
+ bb.min = m_stats.min.cast();
+ bb.max = m_stats.max.cast();
return bb;
}
BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) const
{
BoundingBoxf3 bbox;
- if (this->its.vertices.empty()) {
- // Using the STL faces.
- for (const stl_facet &facet : this->stl.facet_start)
- for (size_t j = 0; j < 3; ++ j)
- bbox.merge(trafo * facet.vertex[j].cast());
- } else {
- // Using the shared vertices should be a bit quicker than using the STL faces.
- for (const stl_vertex &v : this->its.vertices)
- bbox.merge(trafo * v.cast());
- }
+ for (const stl_vertex &v : this->its.vertices)
+ bbox.merge(trafo * v.cast());
return bbox;
}
@@ -582,26 +436,16 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
std::vector src_vertices;
try
{
- if (this->has_shared_vertices()) {
#if REALfloat
- qhull.runQhull("", 3, (int)this->its.vertices.size(), (const realT*)(this->its.vertices.front().data()), "Qt");
+ qhull.runQhull("", 3, (int)this->its.vertices.size(), (const realT*)(this->its.vertices.front().data()), "Qt");
#else
- src_vertices.reserve(this->its.vertices.size() * 3);
- // We will now fill the vector with input points for computation:
- for (const stl_vertex &v : this->its.vertices)
- for (int i = 0; i < 3; ++ i)
- src_vertices.emplace_back(v(i));
- qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
+ src_vertices.reserve(this->its.vertices.size() * 3);
+ // We will now fill the vector with input points for computation:
+ for (const stl_vertex &v : this->its.vertices)
+ for (int i = 0; i < 3; ++ i)
+ src_vertices.emplace_back(v(i));
+ qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
#endif
- } else {
- src_vertices.reserve(this->stl.facet_start.size() * 9);
- // We will now fill the vector with input points for computation:
- for (const stl_facet &f : this->stl.facet_start)
- for (int i = 0; i < 3; ++ i)
- for (int j = 0; j < 3; ++ j)
- src_vertices.emplace_back(f.vertex[i](j));
- qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
- }
}
catch (...)
{
@@ -610,84 +454,77 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
}
// Let's collect results:
- Pointf3s dst_vertices;
- std::vector facets;
- auto facet_list = qhull.facetList().toStdVector();
- for (const orgQhull::QhullFacet& facet : facet_list)
- { // iterate through facets
- orgQhull::QhullVertexSet vertices = facet.vertices();
- for (int i = 0; i < 3; ++i)
- { // iterate through facet's vertices
-
- orgQhull::QhullPoint p = vertices[i].point();
- const auto* coords = p.coordinates();
- dst_vertices.emplace_back(coords[0], coords[1], coords[2]);
+ std::vector dst_vertices;
+ std::vector dst_facets;
+ // Map of QHull's vertex ID to our own vertex ID (pointing to dst_vertices).
+ std::vector map_dst_vertices;
+#ifndef NDEBUG
+ Vec3f centroid = Vec3f::Zero();
+ for (auto pt : this->its.vertices)
+ centroid += pt;
+ centroid /= float(this->its.vertices.size());
+#endif // NDEBUG
+ for (const orgQhull::QhullFacet facet : qhull.facetList()) {
+ // Collect face vertices first, allocate unique vertices in dst_vertices based on QHull's vertex ID.
+ Vec3i indices;
+ int cnt = 0;
+ for (const orgQhull::QhullVertex vertex : facet.vertices()) {
+ int id = vertex.id();
+ assert(id >= 0);
+ if (id >= int(map_dst_vertices.size()))
+ map_dst_vertices.resize(next_highest_power_of_2(size_t(id + 1)), -1);
+ if (int i = map_dst_vertices[id]; i == -1) {
+ // Allocate a new vertex.
+ i = int(dst_vertices.size());
+ map_dst_vertices[id] = i;
+ orgQhull::QhullPoint pt(vertex.point());
+ dst_vertices.emplace_back(pt[0], pt[1], pt[2]);
+ indices[cnt] = i;
+ } else {
+ // Reuse existing vertex.
+ indices[cnt] = i;
+ }
+ if (cnt ++ == 3)
+ break;
+ }
+ assert(cnt == 3);
+ if (cnt == 3) {
+ // QHull sorts vertices of a face lexicographically by their IDs, not by face normals.
+ // Calculate face normal based on the order of vertices.
+ Vec3f n = (dst_vertices[indices(1)] - dst_vertices[indices(0)]).cross(dst_vertices[indices(2)] - dst_vertices[indices(1)]);
+ auto *n2 = facet.getBaseT()->normal;
+ auto d = n.x() * n2[0] + n.y() * n2[1] + n.z() * n2[2];
+#ifndef NDEBUG
+ Vec3f n3 = (dst_vertices[indices(0)] - centroid);
+ auto d3 = n.dot(n3);
+ assert((d < 0.f) == (d3 < 0.f));
+#endif // NDEBUG
+ // Get the face normal from QHull.
+ if (d < 0.f)
+ // Fix face orientation.
+ std::swap(indices[1], indices[2]);
+ dst_facets.emplace_back(indices);
}
- unsigned int size = (unsigned int)dst_vertices.size();
- facets.emplace_back(size - 3, size - 2, size - 1);
}
- TriangleMesh output_mesh(dst_vertices, facets);
- output_mesh.repair();
- return output_mesh;
+ TriangleMesh mesh{ std::move(dst_vertices), std::move(dst_facets) };
+ assert(mesh.stats().manifold());
+ return mesh;
}
std::vector TriangleMesh::slice(const std::vector &z) const
{
// convert doubles to floats
std::vector z_f(z.begin(), z.end());
- assert(this->has_shared_vertices());
return slice_mesh_ex(this->its, z_f, 0.0004f);
}
-void TriangleMesh::require_shared_vertices()
-{
- BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start";
- assert(stl_validate(&this->stl));
- if (! this->repaired)
- this->repair();
- if (this->its.vertices.empty()) {
- BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices";
- stl_generate_shared_vertices(&this->stl, this->its);
- }
- assert(stl_validate(&this->stl, this->its));
- BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end";
-}
-
size_t TriangleMesh::memsize() const
{
- size_t memsize = 8 + this->stl.memsize() + this->its.memsize();
+ size_t memsize = 8 + this->its.memsize() + sizeof(this->m_stats);
return memsize;
}
-// Release optional data from the mesh if the object is on the Undo / Redo stack only. Returns the amount of memory released.
-size_t TriangleMesh::release_optional()
-{
- size_t memsize_released = sizeof(stl_neighbors) * this->stl.neighbors_start.size() + this->its.memsize();
- // The indexed triangle set may be recalculated using the stl_generate_shared_vertices() function.
- this->its.clear();
- // The neighbors structure may be recalculated using the stl_check_facets_exact() function.
- this->stl.neighbors_start.clear();
- return memsize_released;
-}
-
-// Restore optional data possibly released by release_optional().
-void TriangleMesh::restore_optional()
-{
- if (! this->stl.facet_start.empty()) {
- // Save the old stats before calling stl_check_faces_exact, as it may modify the statistics.
- stl_stats stats = this->stl.stats;
- if (this->stl.neighbors_start.empty()) {
- stl_reallocate(&this->stl);
- stl_check_facets_exact(&this->stl);
- }
- if (this->its.vertices.empty())
- stl_generate_shared_vertices(&this->stl, this->its);
- // Restore the old statistics.
- this->stl.stats = stats;
- }
-}
-
// Create a mapping from triangle edge into face.
struct EdgeToFace {
// Index of the 1st vertex of the triangle edge. vertex_low <= vertex_high.
@@ -1063,21 +900,32 @@ Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Transfor
indexed_triangle_set its_make_cube(double xd, double yd, double zd)
{
auto x = float(xd), y = float(yd), z = float(zd);
- indexed_triangle_set mesh;
- mesh.vertices = {{x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0},
- {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z}};
- mesh.indices = {{0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7},
- {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2},
- {2, 6, 5}, {2, 5, 3}, {4, 0, 3}, {4, 3, 5}};
-
- return mesh;
+ return {
+ { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7},
+ {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2},
+ {2, 6, 5}, {2, 5, 3}, {4, 0, 3}, {4, 3, 5} },
+ { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0},
+ {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} }
+ };
}
-TriangleMesh make_cube(double x, double y, double z)
+indexed_triangle_set its_make_prism(float width, float length, float height)
{
- TriangleMesh mesh(its_make_cube(x, y, z));
- mesh.repair();
- return mesh;
+ // We need two upward facing triangles
+ float x = width / 2.f, y = length / 2.f;
+ return {
+ {
+ {0, 1, 2}, // side 1
+ {4, 3, 5}, // side 2
+ {1, 4, 2}, {2, 4, 5}, // roof 1
+ {0, 2, 5}, {0, 5, 3}, // roof 2
+ {3, 4, 1}, {3, 1, 0} // bottom
+ },
+ {
+ {-x, -y, 0.f}, {x, -y, 0.f}, {0.f, -y, height},
+ {-x, y, 0.f}, {x, y, 0.f}, {0.f, y, height},
+ }
+ };
}
// Generate the mesh for a cylinder and return it, using
@@ -1125,14 +973,6 @@ indexed_triangle_set its_make_cylinder(double r, double h, double fa)
return mesh;
}
-TriangleMesh make_cylinder(double r, double h, double fa)
-{
- TriangleMesh mesh{its_make_cylinder(r, h, fa)};
- mesh.repair();
-
- return mesh;
-}
-
indexed_triangle_set its_make_cone(double r, double h, double fa)
{
indexed_triangle_set mesh;
@@ -1159,11 +999,23 @@ indexed_triangle_set its_make_cone(double r, double h, double fa)
return mesh;
}
-TriangleMesh make_cone(double radius, double fa)
+indexed_triangle_set its_make_pyramid(float base, float height)
{
- TriangleMesh mesh(its_make_cone(radius, fa));
- mesh.repair();
- return mesh;
+ float a = base / 2.f;
+ return {
+ {
+ {0, 1, 2},
+ {0, 2, 3},
+ {0, 1, 4},
+ {1, 2, 4},
+ {2, 3, 4},
+ {3, 0, 4}
+ },
+ {
+ {-a, -a, 0}, {a, -a, 0}, {a, a, 0},
+ {-a, a, 0}, {0.f, 0.f, height}
+ }
+ };
}
// Generates mesh for a sphere centered about the origin, using the generated angle
@@ -1224,11 +1076,10 @@ indexed_triangle_set its_make_sphere(double radius, double fa)
return mesh;
}
-TriangleMesh make_sphere(double radius, double fa)
+void its_reverse_all_facets(indexed_triangle_set &its)
{
- TriangleMesh mesh(its_make_sphere(radius, fa));
- mesh.repair();
- return mesh;
+ for (stl_triangle_vertex_indices &face : its.indices)
+ std::swap(face[0], face[1]);
}
void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B)
@@ -1304,10 +1155,40 @@ std::vector its_split(const indexed_triangle_set &its)
return its_split<>(its);
}
+// Number of disconnected patches (faces are connected if they share an edge, shared edge defined with 2 shared vertex indices).
+bool its_number_of_patches(const indexed_triangle_set &its)
+{
+ return its_number_of_patches<>(its);
+}
+bool its_number_of_patches(const indexed_triangle_set &its, const std::vector &face_neighbors)
+{
+ return its_number_of_patches<>(ItsNeighborsWrapper{ its, face_neighbors });
+}
+
+// Same as its_number_of_patches(its) > 1, but faster.
bool its_is_splittable(const indexed_triangle_set &its)
{
return its_is_splittable<>(its);
}
+bool its_is_splittable(const indexed_triangle_set &its, const std::vector &face_neighbors)
+{
+ return its_is_splittable<>(ItsNeighborsWrapper{ its, face_neighbors });
+}
+
+size_t its_num_open_edges(const std::vector &face_neighbors)
+{
+ size_t num_open_edges = 0;
+ for (Vec3i neighbors : face_neighbors)
+ for (int n : neighbors)
+ if (n < 0)
+ ++ num_open_edges;
+ return num_open_edges;
+}
+
+size_t its_num_open_edges(const indexed_triangle_set &its)
+{
+ return its_num_open_edges(its_face_neighbors(its));
+}
void VertexFaceIndex::create(const indexed_triangle_set &its)
{
@@ -1353,4 +1234,79 @@ std::vector its_face_normals(const indexed_triangle_set &its)
return normals;
}
+#if BOOST_ENDIAN_LITTLE_BYTE
+static inline void big_endian_reverse_quads(char*, size_t) {}
+#else // BOOST_ENDIAN_LITTLE_BYTE
+static inline void big_endian_reverse_quads(char *buf, size_t cnt)
+{
+ for (size_t i = 0; i < cnt; i += 4) {
+ std::swap(buf[i], buf[i+3]);
+ std::swap(buf[i+1], buf[i+2]);
+ }
+}
+#endif // BOOST_ENDIAN_LITTLE_BYTE
+
+bool its_write_stl_ascii(const char *file, const char *label, const std::vector &indices, const std::vector &vertices)
+{
+ FILE *fp = boost::nowide::fopen(file, "w");
+ if (fp == nullptr) {
+ BOOST_LOG_TRIVIAL(error) << "its_write_stl_ascii: Couldn't open " << file << " for writing";
+ return false;
+ }
+
+ fprintf(fp, "solid %s\n", label);
+
+ for (const stl_triangle_vertex_indices face : indices) {
+ Vec3f vertex[3] = { vertices[face(0)], vertices[face(1)], vertices[face(2)] };
+ Vec3f normal = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[1]).normalized();
+ fprintf(fp, " facet normal % .8E % .8E % .8E\n", normal(0), normal(1), normal(2));
+ fprintf(fp, " outer loop\n");
+ fprintf(fp, " vertex % .8E % .8E % .8E\n", vertex[0](0), vertex[0](1), vertex[0](2));
+ fprintf(fp, " vertex % .8E % .8E % .8E\n", vertex[1](0), vertex[1](1), vertex[1](2));
+ fprintf(fp, " vertex % .8E % .8E % .8E\n", vertex[2](0), vertex[2](1), vertex[2](2));
+ fprintf(fp, " endloop\n");
+ fprintf(fp, " endfacet\n");
+ }
+
+ fprintf(fp, "endsolid %s\n", label);
+ fclose(fp);
+ return true;
+}
+
+bool its_write_stl_binary(const char *file, const char *label, const std::vector &indices, const std::vector &vertices)
+{
+ FILE *fp = boost::nowide::fopen(file, "wb");
+ if (fp == nullptr) {
+ BOOST_LOG_TRIVIAL(error) << "its_write_stl_binary: Couldn't open " << file << " for writing";
+ return false;
+ }
+
+ {
+ static constexpr const int header_size = 80;
+ std::vector header(header_size, 0);
+ if (int header_len = std::min((label == nullptr) ? 0 : int(strlen(label)), header_size); header_len > 0)
+ ::memcpy(header.data(), label, header_len);
+ ::fwrite(header.data(), header_size, 1, fp);
+ }
+
+ uint32_t nfaces = indices.size();
+ big_endian_reverse_quads(reinterpret_cast(&nfaces), 4);
+ ::fwrite(&nfaces, 4, 1, fp);
+
+ stl_facet f;
+ f.extra[0] = 0;
+ f.extra[1] = 0;
+ for (const stl_triangle_vertex_indices face : indices) {
+ f.vertex[0] = vertices[face(0)];
+ f.vertex[1] = vertices[face(1)];
+ f.vertex[2] = vertices[face(2)];
+ f.normal = (f.vertex[1] - f.vertex[0]).cross(f.vertex[2] - f.vertex[1]).normalized();
+ big_endian_reverse_quads(reinterpret_cast(&f), 48);
+ fwrite(&f, 50, 1, fp);
+ }
+
+ fclose(fp);
+ return true;
+}
+
} // namespace Slic3r
diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp
index 60ab975c4c..d46fb4a8bc 100644
--- a/src/libslic3r/TriangleMesh.hpp
+++ b/src/libslic3r/TriangleMesh.hpp
@@ -15,25 +15,79 @@ namespace Slic3r {
class TriangleMesh;
class TriangleMeshSlicer;
-typedef std::vector TriangleMeshPtrs;
+
+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.
+ // How many edges were united by merging their end points with some other end points in epsilon neighborhood?
+ int edges_fixed = 0;
+ // How many degenerate faces were removed?
+ int degenerate_facets = 0;
+ // How many faces were removed during fixing? Includes degenerate_faces and disconnected faces.
+ int facets_removed = 0;
+ // New faces could only be created with stl_fill_holes() and we ditched stl_fill_holes(), because mostly it does more harm than good.
+ //int facets_added = 0;
+ // How many facets were revesed? Faces are reversed by admesh while it connects patches of triangles togeter and a flipped triangle is encountered.
+ // Also the facets are reversed when a negative volume is corrected by flipping all facets.
+ int facets_reversed = 0;
+ // Edges shared by two triangles, oriented incorrectly.
+ int backwards_edges = 0;
+
+ void clear() { *this = TriangleMeshStats(); }
+
+ TriangleMeshStats merge(const TriangleMeshStats &rhs) const {
+ if (this->number_of_facets == 0)
+ return rhs;
+ else if (rhs.number_of_facets == 0)
+ return *this;
+ else {
+ TriangleMeshStats out;
+ out.number_of_facets = this->number_of_facets + rhs.number_of_facets;
+ out.min = this->min.cwiseMin(rhs.min);
+ out.max = this->max.cwiseMax(rhs.max);
+ out.size = out.max - out.min;
+ out.number_of_parts = this->number_of_parts + rhs.number_of_parts;
+ out.open_edges = this->open_edges + rhs.open_edges;
+ out.volume = this->volume + rhs.volume;
+ 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 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; }
+};
class TriangleMesh
{
public:
- TriangleMesh() : repaired(false) {}
- TriangleMesh(const Pointf3s &points, const std::vector &facets);
+ TriangleMesh() = default;
+ TriangleMesh(const std::vector &vertices, const std::vector &faces);
+ TriangleMesh(std::vector &&vertices, const std::vector &&faces);
explicit TriangleMesh(const indexed_triangle_set &M);
- void clear() { this->stl.clear(); this->its.clear(); this->repaired = false; }
- bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); }
- bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); }
- bool write_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); }
- void repair(bool update_shared_vertices = true);
+ explicit TriangleMesh(indexed_triangle_set &&M);
+ void clear() { this->its.clear(); this->m_stats.clear(); }
+ bool ReadSTLFile(const char* input_file, bool repair = true);
+ bool write_ascii(const char* output_file);
+ bool write_binary(const char* output_file);
float volume();
- void check_topology();
- bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == (int)this->stl.stats.number_of_facets; }
void WriteOBJFile(const char* output_file) const;
void scale(float factor);
- void scale(const Vec3d &versor);
+ void scale(const Vec3f &versor);
void translate(float x, float y, float z);
void translate(const Vec3f &displacement);
void rotate(float angle, const Axis &axis);
@@ -41,15 +95,17 @@ public:
void rotate_x(float angle) { this->rotate(angle, X); }
void rotate_y(float angle) { this->rotate(angle, Y); }
void rotate_z(float angle) { this->rotate(angle, Z); }
- void mirror(const Axis &axis);
+ void mirror(const Axis axis);
void mirror_x() { this->mirror(X); }
void mirror_y() { this->mirror(Y); }
void mirror_z() { this->mirror(Z); }
void transform(const Transform3d& t, bool fix_left_handed = false);
void transform(const Matrix3d& t, bool fix_left_handed = false);
+ // Flip triangles, negate volume.
+ void flip_triangles();
void align_to_origin();
void rotate(double angle, Point* center);
- TriangleMeshPtrs split() const;
+ std::vector split() const;
void merge(const TriangleMesh &mesh);
ExPolygons horizontal_projection() const;
// 2D convex hull of a 3D mesh projected into the Z=0 plane.
@@ -58,37 +114,33 @@ public:
// Returns the bbox of this TriangleMesh transformed by the given transformation
BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const;
// Return the size of the mesh in coordinates.
- Vec3d size() const { return stl.stats.size.cast(); }
+ Vec3d size() const { return m_stats.size.cast(); }
/// Return the center of the related bounding box.
Vec3d center() const { return this->bounding_box().center(); }
// Returns the convex hull of this TriangleMesh
TriangleMesh convex_hull_3d() const;
// Slice this mesh at the provided Z levels and return the vector
std::vector slice(const std::vector& z) const;
- void reset_repair_stats();
- bool needed_repair() const;
- void require_shared_vertices();
- bool has_shared_vertices() const { return ! this->its.vertices.empty(); }
- size_t facets_count() const { return this->stl.stats.number_of_facets; }
+ size_t facets_count() const { assert(m_stats.number_of_facets == this->its.indices.size()); return m_stats.number_of_facets; }
bool empty() const { return this->facets_count() == 0; }
- bool is_splittable() const;
+ bool repaired() const;
+ bool is_splittable() const;
// Estimate of the memory occupied by this structure, important for keeping an eye on the Undo / Redo stack allocation.
size_t memsize() const;
- // Release optional data from the mesh if the object is on the Undo / Redo stack only. Returns the amount of memory released.
- size_t release_optional();
- // Restore optional data possibly released by release_optional().
- void restore_optional();
- const stl_stats& stats() const { return this->stl.stats; }
+ // Used by the Undo / Redo stack, legacy interface. As of now there is nothing cached at TriangleMesh,
+ // but we may decide to cache some data in the future (for example normals), thus we keep the interface in place.
+ // Release optional data from the mesh if the object is on the Undo / Redo stack only. Returns the amount of memory released.
+ size_t release_optional() { return 0; }
+ // Restore optional data possibly released by release_optional().
+ void restore_optional() {}
+
+ const TriangleMeshStats& stats() const { return m_stats; }
indexed_triangle_set its;
- bool repaired;
-
-//private:
- stl_file stl;
private:
- std::deque find_unvisited_neighbors(std::vector &facet_visited) const;
+ TriangleMeshStats m_stats;
};
// Index of face indices incident with a vertex index.
@@ -148,8 +200,18 @@ bool its_store_triangle(const indexed_triangle_set &its, const char *obj_filenam
bool its_store_triangles(const indexed_triangle_set &its, const char *obj_filename, const std::vector& triangles);
std::vector its_split(const indexed_triangle_set &its);
+std::vector its_split(const indexed_triangle_set &its, std::vector &face_neighbors);
+// Number of disconnected patches (faces are connected if they share an edge, shared edge defined with 2 shared vertex indices).
+bool its_number_of_patches(const indexed_triangle_set &its);
+bool its_number_of_patches(const indexed_triangle_set &its, const std::vector &face_neighbors);
+// Same as its_number_of_patches(its) > 1, but faster.
bool its_is_splittable(const indexed_triangle_set &its);
+bool its_is_splittable(const indexed_triangle_set &its, const std::vector &face_neighbors);
+
+// Calculate number of unconnected face edges. There should be no unconnected edge in a manifold mesh.
+size_t its_num_open_edges(const indexed_triangle_set &its);
+size_t its_num_open_edges(const std::vector &face_neighbors);
// Shrink the vectors of its.vertices and its.faces to a minimum size by reallocating the two vectors.
void its_shrink_to_fit(indexed_triangle_set &its);
@@ -217,13 +279,23 @@ inline Vec3f its_face_normal(const indexed_triangle_set &its, const int face_idx
{ return its_face_normal(its, its.indices[face_idx]); }
indexed_triangle_set its_make_cube(double x, double y, double z);
-TriangleMesh make_cube(double x, double y, double z);
+indexed_triangle_set its_make_prism(float width, float length, float height);
indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360));
-TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360));
-TriangleMesh make_cone(double r, double h, double fa=(2*PI/360));
+indexed_triangle_set its_make_pyramid(float base, float height);
indexed_triangle_set its_make_sphere(double radius, double fa);
-TriangleMesh make_sphere(double rho, double fa=(2*PI/360));
+
+inline TriangleMesh make_cube(double x, double y, double z) { return TriangleMesh(its_make_cube(x, y, z)); }
+inline TriangleMesh make_prism(float width, float length, float height) { return TriangleMesh(its_make_prism(width, length, height)); }
+inline TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360)) { return TriangleMesh{its_make_cylinder(r, h, fa)}; }
+inline TriangleMesh make_cone(double r, double h, double fa=(2*PI/360)) { return TriangleMesh(its_make_cone(r, h, fa)); }
+inline TriangleMesh make_pyramid(float base, float height) { return TriangleMesh(its_make_pyramid(base, height)); }
+inline TriangleMesh make_sphere(double rho, double fa=(2*PI/360)) { return TriangleMesh(its_make_sphere(rho, fa)); }
+
+bool its_write_stl_ascii(const char *file, const char *label, const std::vector &indices, const std::vector &vertices);
+inline bool its_write_stl_ascii(const char *file, const char *label, const indexed_triangle_set &its) { return its_write_stl_ascii(file, label, its.indices, its.vertices); }
+bool its_write_stl_binary(const char *file, const char *label, const std::vector &indices, const std::vector &vertices);
+inline bool its_write_stl_binary(const char *file, const char *label, const indexed_triangle_set &its) { return its_write_stl_binary(file, label, its.indices, its.vertices); }
inline BoundingBoxf3 bounding_box(const TriangleMesh &m) { return m.bounding_box(); }
inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its)
@@ -248,18 +320,12 @@ inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its)
namespace cereal {
template struct specialize {};
template void load(Archive &archive, Slic3r::TriangleMesh &mesh) {
- stl_file &stl = mesh.stl;
- stl.stats.type = inmemory;
- archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
- stl_allocate(&stl);
- archive.loadBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
- stl_get_size(&stl);
- mesh.repair();
+ archive.loadBinary(reinterpret_cast(const_cast(&mesh.stats())), sizeof(Slic3r::TriangleMeshStats));
+ archive(mesh.its.indices, mesh.its.vertices);
}
template void save(Archive &archive, const Slic3r::TriangleMesh &mesh) {
- const stl_file& stl = mesh.stl;
- archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
- archive.saveBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
+ archive.saveBinary(reinterpret_cast(&mesh.stats()), sizeof(Slic3r::TriangleMeshStats));
+ archive(mesh.its.indices, mesh.its.vertices);
}
}
diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp
index aa2763968d..d3c9a49b5c 100644
--- a/src/libslic3r/TriangleMeshSlicer.cpp
+++ b/src/libslic3r/TriangleMeshSlicer.cpp
@@ -1967,7 +1967,8 @@ static void triangulate_slice(
int num_original_vertices,
// Z height of the slice.
float z,
- bool triangulate)
+ bool triangulate,
+ bool normals_down)
{
sort_remove_duplicates(slice_vertices);
@@ -1988,7 +1989,7 @@ static void triangulate_slice(
std::vector map_duplicate_vertex(int(its.vertices.size()) - num_original_vertices, -1);
int i = 0;
int k = 0;
- for (; i < int(map_vertex_to_index.size()); ++ i) {
+ for (; i < int(map_vertex_to_index.size());) {
map_vertex_to_index[k ++] = map_vertex_to_index[i];
const Vec2f &ipos = map_vertex_to_index[i].first;
const int iidx = map_vertex_to_index[i].second;
@@ -2003,6 +2004,7 @@ static void triangulate_slice(
// map to the first vertex
map_duplicate_vertex[jidx - num_original_vertices] = iidx;
}
+ i = j;
}
map_vertex_to_index.erase(map_vertex_to_index.begin() + k, map_vertex_to_index.end());
for (stl_triangle_vertex_indices &f : its.indices)
@@ -2013,7 +2015,7 @@ static void triangulate_slice(
if (triangulate) {
size_t idx_vertex_new_first = its.vertices.size();
- Pointf3s triangles = triangulate_expolygons_3d(make_expolygons_simple(lines), z, true);
+ Pointf3s triangles = triangulate_expolygons_3d(make_expolygons_simple(lines), z, normals_down);
for (size_t i = 0; i < triangles.size(); ) {
stl_triangle_vertex_indices facet;
for (size_t j = 0; j < 3; ++ j) {
@@ -2049,6 +2051,33 @@ static void triangulate_slice(
// its_remove_degenerate_faces(its);
}
+void project_mesh(
+ const indexed_triangle_set &mesh,
+ const Transform3d &trafo,
+ Polygons *out_top,
+ Polygons *out_bottom,
+ std::function throw_on_cancel)
+{
+ std::vector top, bottom;
+ std::vector zs { -1e10, 1e10 };
+ slice_mesh_slabs(mesh, zs, trafo, out_top ? &top : nullptr, out_bottom ? &bottom : nullptr, throw_on_cancel);
+ if (out_top)
+ *out_top = std::move(top.front());
+ if (out_bottom)
+ *out_bottom = std::move(bottom.back());
+}
+
+Polygons project_mesh(
+ const indexed_triangle_set &mesh,
+ const Transform3d &trafo,
+ std::function