Fixing projection of bottom surfaces in MM segmentation and for

support blockers / enforcers.

All slicing functions shall produce consistent results with the same mesh, same transformation matrix and slicing parameters.
Namely, slice_mesh_slabs() shall produce consistent results with slice_mesh() and slice_mesh_ex() in the sense, that projections made by
slice_mesh_slabs() shall fall onto slicing planes produced by slice_mesh().

Before this commit, slice_mesh_slabs() projected bottom facing faces upwards to its coplanar slicing plane,
which is different from how slice_mesh() or slice_mesh_ex() work, leading to ignored support enforcer / blocker strokes.
This commit is contained in:
Vojtech Bubnik 2021-10-28 15:07:29 +02:00
parent 93e91bcacb
commit b6c4e94d81
4 changed files with 71 additions and 37 deletions

View file

@ -2284,6 +2284,7 @@ void PrintObject::project_and_append_custom_facets(
seam, out);
else {
std::vector<Polygons> projected;
// Support blockers or enforcers. Project downward facing painted areas upwards to their respective slicing plane.
slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){});
// Merge these projections with the output, layer by layer.
assert(! projected.empty());

View file

@ -1645,26 +1645,29 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
polygons_append(contact_polygons, diff_polygons);
} // for each layer.region
if (has_enforcer) {
// Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
#ifdef SLIC3R_DEBUG
ExPolygons enforcers_united = union_ex(annotations.enforcers_layers[layer_id]);
#endif // SLIC3R_DEBUG
enforcer_polygons = diff(intersection(layer.lslices, annotations.enforcers_layers[layer_id]),
// Inflate just a tiny bit to avoid intersection of the overhang areas with the object.
expand(lower_layer_polygons, 0.05f * no_interface_offset, 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),
{ { layer.lslices, { "layer.lslices", "gray", 0.2f } },
{ { union_ex(lower_layer_polygons) }, { "lower_layer_polygons", "green", 0.5f } },
{ enforcers_united, { "enforcers", "blue", 0.5f } },
{ { union_safety_offset_ex(enforcer_polygons) }, { "new_contacts", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
#endif /* SLIC3R_DEBUG */
polygons_append(overhang_polygons, enforcer_polygons);
slices_margin_update(std::min(lower_layer_offset, float(scale_(gap_xy))), no_interface_offset);
polygons_append(contact_polygons, diff(enforcer_polygons, slices_margin.all_polygons.empty() ? slices_margin.polygons : slices_margin.all_polygons));
if (has_enforcer)
if (const Polygons &enforcer_polygons_src = annotations.enforcers_layers[layer_id]; ! enforcer_polygons_src.empty()) {
// Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
#ifdef SLIC3R_DEBUG
ExPolygons enforcers_united = union_ex(enforcer_polygons_src);
#endif // SLIC3R_DEBUG
enforcer_polygons = diff(intersection(layer.lslices, enforcer_polygons_src),
// Inflate just a tiny bit to avoid intersection of the overhang areas with the object.
expand(lower_layer_polygons, 0.05f * no_interface_offset, 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),
{ { layer.lslices, { "layer.lslices", "gray", 0.2f } },
{ { union_ex(lower_layer_polygons) }, { "lower_layer_polygons", "green", 0.5f } },
{ enforcers_united, { "enforcers", "blue", 0.5f } },
{ { union_safety_offset_ex(enforcer_polygons) }, { "new_contacts", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
#endif /* SLIC3R_DEBUG */
if (! enforcer_polygons.empty()) {
polygons_append(overhang_polygons, enforcer_polygons);
slices_margin_update(std::min(lower_layer_offset, float(scale_(gap_xy))), no_interface_offset);
polygons_append(contact_polygons, diff(enforcer_polygons, slices_margin.all_polygons.empty() ? slices_margin.polygons : slices_margin.all_polygons));
}
}
}
}
return std::make_tuple(std::move(overhang_polygons), std::move(contact_polygons), std::move(enforcer_polygons), no_interface_offset);
}

View file

@ -458,23 +458,33 @@ void slice_facet_with_slabs(
// Slicing a horizontal triangle with a slicing plane. The triangle has to be upwards facing for ProjectionFromTop
// and downwards facing for ! ProjectionFromTop.
assert(min_layer != max_layer);
size_t line_id = min_layer - zs.begin();
for (int iedge = 0; iedge < 3; ++ iedge)
if (facet_neighbors(iedge) == -1) {
int i = iedge;
int j = next_idx_modulo(i, 3);
assert(vertices[i].z() == zs[line_id]);
assert(vertices[j].z() == zs[line_id]);
IntersectionLine il {
{ to_2d(vertices[i]).cast<coord_t>(), to_2d(vertices[j]).cast<coord_t>() },
indices(i), indices(j), -1, -1,
ProjectionFromTop ? IntersectionLine::FacetEdgeType::Bottom : IntersectionLine::FacetEdgeType::Top
};
// Don't flip the FacetEdgeType::Top edge, it will be flipped when chaining.
// if (! ProjectionFromTop) il.reverse();
boost::lock_guard<std::mutex> l(lines_mutex[line_id % lines_mutex.size()]);
lines.at_slice[line_id].emplace_back(il);
}
// Slicing plane with which the triangle is coplanar.
size_t slice_id = min_layer - zs.begin();
#if 0
// Project the coplanar bottom facing triangles to their slicing plane for both top and bottom facing surfaces.
// This behavior is different from slice_mesh() / slice_mesh_ex(), which do not slice bottom facing faces exactly on slicing plane.
size_t line_id = slice_id;
#else
// Project the coplanar bottom facing triangles to the plane above the slicing plane to match the behavior of slice_mesh() / slice_mesh_ex(),
// where the slicing plane slices the top facing surfaces, but misses the bottom facing surfaces.
if (size_t line_id = ProjectionFromTop ? slice_id : slice_id + 1; ProjectionFromTop || line_id < lines.at_slice.size())
#endif
for (int iedge = 0; iedge < 3; ++ iedge)
if (facet_neighbors(iedge) == -1) {
int i = iedge;
int j = next_idx_modulo(i, 3);
assert(vertices[i].z() == zs[slice_id]);
assert(vertices[j].z() == zs[slice_id]);
IntersectionLine il {
{ to_2d(vertices[i]).cast<coord_t>(), to_2d(vertices[j]).cast<coord_t>() },
indices(i), indices(j), -1, -1,
ProjectionFromTop ? IntersectionLine::FacetEdgeType::Bottom : IntersectionLine::FacetEdgeType::Top
};
// Don't flip the FacetEdgeType::Top edge, it will be flipped when chaining.
// if (! ProjectionFromTop) il.reverse();
boost::lock_guard<std::mutex> l(lines_mutex[line_id % lines_mutex.size()]);
lines.at_slice[line_id].emplace_back(il);
}
} else {
// Triangle is completely between two slicing planes, the triangle may or may not be horizontal, which
// does not matter for the processing of such a triangle.
@ -539,6 +549,7 @@ void slice_facet_with_slabs(
assert(il.b_id == indices(next_idx_modulo(edge_id, 3)));
} else {
// The edge is oriented CW along the face perimeter.
assert(type == FacetSliceType::Slicing);
assert(il.edge_type == IntersectionLine::FacetEdgeType::Top);
edge_id = il.b_id == indices(0) ? 0 : il.b_id == indices(1) ? 1 : 2;
assert(il.b_id == indices(edge_id));
@ -555,8 +566,11 @@ void slice_facet_with_slabs(
int num_on_plane = (mesh_vertices[neighbor(0)].z() == z) + (mesh_vertices[neighbor(1)].z() == z) + (mesh_vertices[neighbor(2)].z() == z);
assert(num_on_plane == 2 || num_on_plane == 3);
#endif // NDEBUG
#if 0
if (mesh_vertices[neighbor(0)].z() == z && mesh_vertices[neighbor(1)].z() == z && mesh_vertices[neighbor(2)].z() == z) {
// The neighbor triangle is horizontal.
// Assign the horizontal projections to slicing planes differently from the usual triangle mesh slicing:
// Slicing plane slices top surfaces when projecting from top, it slices bottom surfaces when projecting from bottom.
// Is the corner convex or concave?
if (il.edge_type == (ProjectionFromTop ? IntersectionLine::FacetEdgeType::Top : IntersectionLine::FacetEdgeType::Bottom)) {
// Convex corner. Add this edge to both slabs, the edge is a boundary edge of both the projection patch below and
@ -567,7 +581,12 @@ void slice_facet_with_slabs(
// Concave corner. Ignore this edge, it is internal to the projection patch.
type = FacetSliceType::Cutting;
}
} else if (il.edge_type == IntersectionLine::FacetEdgeType::Top) {
} else
#else
// Project the coplanar bottom facing triangles to the plane above the slicing plane to match the behavior of slice_mesh() / slice_mesh_ex(),
// where the slicing plane slices the top facing surfaces, but misses the bottom facing surfaces.
#endif
if (il.edge_type == IntersectionLine::FacetEdgeType::Top) {
// Indicate that the edge belongs to both the slab below and above the plane.
assert(type == FacetSliceType::Slicing);
il.edge_type = IntersectionLine::FacetEdgeType::TopBottom;

View file

@ -46,6 +46,17 @@ struct MeshSlicingParamsEx : public MeshSlicingParams
double resolution { 0 };
};
// All the following slicing functions shall produce consistent results with the same mesh, same transformation matrix and slicing parameters.
// Namely, slice_mesh_slabs() shall produce consistent results with slice_mesh() and slice_mesh_ex() in the sense, that projections made by
// slice_mesh_slabs() shall fall onto slicing planes produced by slice_mesh().
//
// If a slicing plane slices a horizontal face of a mesh exactly,
// an upward facing horizontal face is is considered on slicing plane,
// while a downward facing horizontal face is considered not on slicing plane.
//
// slice_mesh_slabs() thus projects an upward facing horizontal slice to the slicing plane,
// while slice_mesh_slabs() projects a downward facing horizontal slice to the slicing plane above if it exists.
std::vector<Polygons> slice_mesh(
const indexed_triangle_set &mesh,
const std::vector<float> &zs,