From 8ba230db9f37fe3ae576b9f13fcfef948cdcfa06 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 23 Feb 2021 12:23:06 +0100 Subject: [PATCH] Fix of Paint on support ignores some paints. #5948 When projecting the horizontal or nearly horizontal support enforcers or blockers into object layers, the projection may fall on a layer above or below the layer where it should in case the nearly horizontal support enforcer or blocker triangles are intersecting the slicing plane of one of the object layers. Due to numerical issues, projection of the support blocker or enforcer triangles may not fall to the same side of the slicing plane as when slicing the object. To make the projection robust, horizontal triangles are newly projected to both the layer below and above if they are close to the object slicing plane. --- src/libslic3r/PrintObject.cpp | 66 +++++++++++++++++++++---------- src/libslic3r/SupportMaterial.cpp | 14 ++++++- 2 files changed, 58 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 46873ff06..6dc8478a5 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2801,8 +2801,9 @@ void PrintObject::project_and_append_custom_facets( const Transform3f& tr1 = mv->get_matrix().cast(); const Transform3f& tr2 = this->trafo().cast(); const Transform3f tr = tr2 * tr1; - const float tr_det_sign = (tr.matrix().determinant() > 0. ? 1.f : -1.f); - + const float tr_det_sign = (tr.matrix().determinant() > 0. ? 1.f : -1.f); + const Vec2f center = unscaled(this->center_offset()); + ConstLayerPtrsAdaptor layers = this->layers(); // The projection will be at most a pentagon. Let's minimize heap // reallocations by saving in in the following struct. @@ -2810,10 +2811,17 @@ void PrintObject::project_and_append_custom_facets( // and they can be moved from to create an ExPolygon later. struct LightPolygon { LightPolygon() { pts.reserve(5); } + LightPolygon(const std::array& tri) { + pts.reserve(3); + pts.emplace_back(scaled(tri.front())); + pts.emplace_back(scaled(tri[1])); + pts.emplace_back(scaled(tri.back())); + } + Points pts; void add(const Vec2f& pt) { - pts.emplace_back(scale_(pt.x()), scale_(pt.y())); + pts.emplace_back(scaled(pt)); assert(pts.size() <= 5); } }; @@ -2831,7 +2839,7 @@ void PrintObject::project_and_append_custom_facets( // Iterate over all triangles. tbb::parallel_for( tbb::blocked_range(0, custom_facets.indices.size()), - [&](const tbb::blocked_range& range) { + [center, &custom_facets, &tr, tr_det_sign, seam, layers, &projections_of_triangles](const tbb::blocked_range& range) { for (size_t idx = range.begin(); idx < range.end(); ++ idx) { std::array facet; @@ -2857,30 +2865,43 @@ void PrintObject::project_and_append_custom_facets( }); std::array trianglef; - for (int i=0; i<3; ++i) { - trianglef[i] = Vec2f(facet[i].x(), facet[i].y()); - trianglef[i] -= Vec2f(unscale(this->center_offset().x()), - unscale(this->center_offset().y())); - } + for (int i=0; i<3; ++i) + trianglef[i] = to_2d(facet[i]) - center; // Find lowest slice not below the triangle. - auto it = std::lower_bound(layers().begin(), layers().end(), facet[0].z()+EPSILON, + auto it = std::lower_bound(layers.begin(), layers.end(), facet[0].z()+EPSILON, [](const Layer* l1, float z) { return l1->slice_z < z; }); // Count how many projections will be generated for this triangle // and allocate respective amount in projections_of_triangles. - projections_of_triangles[idx].first_layer_id = it-layers().begin(); - size_t last_layer_id = projections_of_triangles[idx].first_layer_id; + size_t first_layer_id = projections_of_triangles[idx].first_layer_id = it - layers.begin(); + size_t last_layer_id = first_layer_id; // The cast in the condition below is important. The comparison must // be an exact opposite of the one lower in the code where // the polygons are appended. And that one is on floats. - while (last_layer_id + 1 < layers().size() - && float(layers()[last_layer_id]->slice_z) <= facet[2].z()) + while (last_layer_id + 1 < layers.size() + && float(layers[last_layer_id]->slice_z) <= facet[2].z()) ++last_layer_id; - projections_of_triangles[idx].polygons.resize( - last_layer_id - projections_of_triangles[idx].first_layer_id + 1); + + if (first_layer_id == last_layer_id) { + // The triangle fits just a single slab, just project it. This also avoids division by zero for horizontal triangles. + float dz = facet[2].z() - facet[0].z(); + assert(dz >= 0); + // The face is nearly horizontal and it crosses the slicing plane at first_layer_id - 1. + // Rather add this face to both the planes. + bool add_below = dz < float(2. * EPSILON) && first_layer_id > 0 && layers[first_layer_id - 1]->slice_z > facet[0].z() - EPSILON; + projections_of_triangles[idx].polygons.reserve(add_below ? 2 : 1); + projections_of_triangles[idx].polygons.emplace_back(trianglef); + if (add_below) { + -- projections_of_triangles[idx].first_layer_id; + projections_of_triangles[idx].polygons.emplace_back(trianglef); + } + continue; + } + + projections_of_triangles[idx].polygons.resize(last_layer_id - first_layer_id + 1); // Calculate how to move points on triangle sides per unit z increment. Vec2f ta(trianglef[1] - trianglef[0]); @@ -2896,7 +2917,7 @@ void PrintObject::project_and_append_custom_facets( bool stop = false; // Project a sub-polygon on all slices intersecting the triangle. - while (it != layers().end()) { + while (it != layers.end()) { const float z = float((*it)->slice_z); // Projections of triangle sides intersections with slices. @@ -2914,7 +2935,7 @@ void PrintObject::project_and_append_custom_facets( } // This slice is above the triangle already. - if (z > facet[2].z() || it+1 == layers().end()) { + if (z > facet[2].z() || it+1 == layers.end()) { proj->add(trianglef[2]); stop = true; } @@ -2944,14 +2965,19 @@ void PrintObject::project_and_append_custom_facets( }); // end of parallel_for // Make sure that the output vector can be used. - expolys.resize(layers().size()); + expolys.resize(layers.size()); // Now append the collected polygons to respective layers. for (auto& trg : projections_of_triangles) { int layer_id = int(trg.first_layer_id); - for (const LightPolygon& poly : trg.polygons) { + for (LightPolygon &poly : trg.polygons) { if (layer_id >= int(expolys.size())) break; // part of triangle could be projected above top layer + assert(! poly.pts.empty()); + // The resulting triangles are fed to the Clipper library, which seem to handle flipped triangles well. +// if (cross2(Vec2d((poly.pts[1] - poly.pts[0]).cast()), Vec2d((poly.pts[2] - poly.pts[1]).cast())) < 0) +// std::swap(poly.pts.front(), poly.pts.back()); + expolys[layer_id].emplace_back(std::move(poly.pts)); ++layer_id; } diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index ee8c7f219..e7685db1f 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -1408,8 +1408,18 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ const ExPolygons &enforcer = enforcers[layer_id]; if (! enforcer.empty()) { // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes. - Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(std::move(enforcer))), - offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)); +#ifdef SLIC3R_DEBUG + ExPolygons enforcers_united = union_ex(to_polygons(enforcer), false); +#endif // SLIC3R_DEBUG + Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(std::move(enforcer))), + offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)); +#ifdef SLIC3R_DEBUG + SVG::export_expolygons(debug_out_path("support-top-contacts-enforcers-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), + { { { union_ex(layerm_polygons, false) }, { "layerm_polygons", "gray", 0.2f } }, + { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "green", 0.5f } }, + { enforcers_united, { "enforcers", "blue", 0.5f } }, + { { union_ex(new_contacts, true) }, { "new_contacts", "red", "black", "", scaled(0.1f), 0.5f } } }); +#endif /* SLIC3R_DEBUG */ if (! new_contacts.empty()) { if (diff_polygons.empty()) diff_polygons = std::move(new_contacts);