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.
This commit is contained in:
Vojtech Bubnik 2021-02-23 12:23:06 +01:00
parent e1c201e714
commit 8ba230db9f
2 changed files with 58 additions and 22 deletions

View File

@ -2801,8 +2801,9 @@ void PrintObject::project_and_append_custom_facets(
const Transform3f& tr1 = mv->get_matrix().cast<float>();
const Transform3f& tr2 = this->trafo().cast<float>();
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<float>(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<Vec2f, 3>& tri) {
pts.reserve(3);
pts.emplace_back(scaled<coord_t>(tri.front()));
pts.emplace_back(scaled<coord_t>(tri[1]));
pts.emplace_back(scaled<coord_t>(tri.back()));
}
Points pts;
void add(const Vec2f& pt) {
pts.emplace_back(scale_(pt.x()), scale_(pt.y()));
pts.emplace_back(scaled<coord_t>(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<size_t>(0, custom_facets.indices.size()),
[&](const tbb::blocked_range<size_t>& range) {
[center, &custom_facets, &tr, tr_det_sign, seam, layers, &projections_of_triangles](const tbb::blocked_range<size_t>& range) {
for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
std::array<Vec3f, 3> facet;
@ -2857,30 +2865,43 @@ void PrintObject::project_and_append_custom_facets(
});
std::array<Vec2f, 3> trianglef;
for (int i=0; i<3; ++i) {
trianglef[i] = Vec2f(facet[i].x(), facet[i].y());
trianglef[i] -= Vec2f(unscale<float>(this->center_offset().x()),
unscale<float>(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<double>()), Vec2d((poly.pts[2] - poly.pts[1]).cast<double>())) < 0)
// std::swap(poly.pts.front(), poly.pts.back());
expolys[layer_id].emplace_back(std::move(poly.pts));
++layer_id;
}

View File

@ -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<coord_t>(0.1f), 0.5f } } });
#endif /* SLIC3R_DEBUG */
if (! new_contacts.empty()) {
if (diff_polygons.empty())
diff_polygons = std::move(new_contacts);