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:
parent
93e91bcacb
commit
b6c4e94d81
4 changed files with 71 additions and 37 deletions
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue