Fix of SPE-1752, GH #10736:
Extended the bridge expansion algorithm to expand into sparse infill
by a maximum of 1 perimeter width. This solves a situation, where
the "ensure vertical wall thickness" region is filtered out as
too narrow to be important for print stability while the bridge expansion
only happened into the "ensure vertical wall thickness" regions before.
Partial revert of cf6b6ff4e4
, reverts
Fix SPE-1726 - hole between bridges and perimeters caused by thin internal fill surface
GithubIssue https://github.com/prusa3d/PrusaSlicer/issues/10231#issuecomment-1546779200
During sorting of surfaces into top, bottom and internal, expand bottom bridges by one spacing unit, to ensure better anchoring
This commit is contained in:
parent
e035aa8225
commit
02f69575ef
@ -480,10 +480,8 @@ std::vector<Polygons> expand_expolygons(const ExPolygons &src, const ExPolygons
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<ExPolygon> expand_merge_expolygons(ExPolygons &&src, const ExPolygons &boundary, const RegionExpansionParameters ¶ms)
|
||||
std::vector<ExPolygon> merge_expansions_into_expolygons(ExPolygons &&src, std::vector<RegionExpansion> &&expanded)
|
||||
{
|
||||
// expanded regions are sorted by boundary id and source id
|
||||
std::vector<RegionExpansion> expanded = propagate_waves(src, boundary, params);
|
||||
// expanded regions will be merged into source regions, thus they will be re-sorted by source id.
|
||||
std::sort(expanded.begin(), expanded.end(), [](const auto &l, const auto &r) { return l.src_id < r.src_id; });
|
||||
uint32_t last = 0;
|
||||
@ -535,5 +533,12 @@ std::vector<ExPolygon> expand_merge_expolygons(ExPolygons &&src, const ExPolygon
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<ExPolygon> expand_merge_expolygons(ExPolygons &&src, const ExPolygons &boundary, const RegionExpansionParameters ¶ms)
|
||||
{
|
||||
// expanded regions are sorted by boundary id and source id
|
||||
std::vector<RegionExpansion> expanded = propagate_waves(src, boundary, params);
|
||||
return merge_expansions_into_expolygons(std::move(src), std::move(expanded));
|
||||
}
|
||||
|
||||
} // Algorithm
|
||||
} // Slic3r
|
||||
|
@ -72,6 +72,7 @@ struct RegionExpansion
|
||||
};
|
||||
|
||||
std::vector<RegionExpansion> propagate_waves(const WaveSeeds &seeds, const ExPolygons &boundary, const RegionExpansionParameters ¶ms);
|
||||
std::vector<RegionExpansion> propagate_waves(const ExPolygons &src, const ExPolygons &boundary, const RegionExpansionParameters ¶ms);
|
||||
|
||||
std::vector<RegionExpansion> propagate_waves(const ExPolygons &src, const ExPolygons &boundary,
|
||||
// Scaled expansion value
|
||||
@ -106,6 +107,9 @@ std::vector<Polygons> expand_expolygons(const ExPolygons &src, const ExPolygons
|
||||
// Don't take more than max_nr_steps for small expansion_step.
|
||||
size_t max_nr_steps);
|
||||
|
||||
// Merge src with expansions, return the merged expolygons.
|
||||
std::vector<ExPolygon> merge_expansions_into_expolygons(ExPolygons &&src, std::vector<RegionExpansion> &&expanded);
|
||||
|
||||
std::vector<ExPolygon> expand_merge_expolygons(ExPolygons &&src, const ExPolygons &boundary, const RegionExpansionParameters ¶ms);
|
||||
|
||||
} // Algorithm
|
||||
|
@ -170,7 +170,9 @@ static ExPolygons fill_surfaces_extract_expolygons(Surfaces &surfaces, std::init
|
||||
Surfaces expand_bridges_detect_orientations(
|
||||
Surfaces &surfaces,
|
||||
ExPolygons &shells,
|
||||
const Algorithm::RegionExpansionParameters &expansion_params,
|
||||
const Algorithm::RegionExpansionParameters &expansion_params_into_solid_infill,
|
||||
ExPolygons &sparse,
|
||||
const Algorithm::RegionExpansionParameters &expansion_params_into_sparse_infill,
|
||||
const float closing_radius)
|
||||
{
|
||||
using namespace Slic3r::Algorithm;
|
||||
@ -181,8 +183,23 @@ Surfaces expand_bridges_detect_orientations(
|
||||
return {};
|
||||
|
||||
// Calculate bridge anchors and their expansions in their respective shell region.
|
||||
WaveSeeds bridge_anchors = wave_seeds(bridges_ex, shells, expansion_params.tiny_expansion, true);
|
||||
std::vector<RegionExpansionEx> bridge_expansions = propagate_waves_ex(bridge_anchors, shells, expansion_params);
|
||||
WaveSeeds bridge_anchors = wave_seeds(bridges_ex, shells, expansion_params_into_solid_infill.tiny_expansion, true);
|
||||
std::vector<RegionExpansionEx> bridge_expansions = propagate_waves_ex(bridge_anchors, shells, expansion_params_into_solid_infill);
|
||||
bool expanded_into_shells = ! bridge_expansions.empty();
|
||||
bool expanded_into_sparse = false;
|
||||
{
|
||||
WaveSeeds bridge_anchors_sparse = wave_seeds(bridges_ex, sparse, expansion_params_into_sparse_infill.tiny_expansion, true);
|
||||
std::vector<RegionExpansionEx> bridge_expansions_sparse = propagate_waves_ex(bridge_anchors_sparse, sparse, expansion_params_into_sparse_infill);
|
||||
if (! bridge_expansions_sparse.empty()) {
|
||||
expanded_into_sparse = true;
|
||||
for (WaveSeed &seed : bridge_anchors_sparse)
|
||||
seed.boundary += uint32_t(shells.size());
|
||||
for (RegionExpansionEx &expansion : bridge_expansions_sparse)
|
||||
expansion.boundary_id += uint32_t(shells.size());
|
||||
append(bridge_anchors, std::move(bridge_anchors_sparse));
|
||||
append(bridge_expansions, std::move(bridge_expansions_sparse));
|
||||
}
|
||||
}
|
||||
|
||||
// Cache for detecting bridge orientation and merging regions with overlapping expansions.
|
||||
struct Bridge {
|
||||
@ -259,7 +276,7 @@ Surfaces expand_bridges_detect_orientations(
|
||||
for (; it_bridge_anchor != bridge_anchors.end() && it_bridge_anchor->src == bridge_id; ++ it_bridge_anchor) {
|
||||
if (last_anchor_id != int(it_bridge_anchor->boundary)) {
|
||||
last_anchor_id = int(it_bridge_anchor->boundary);
|
||||
append(anchor_areas, to_polygons(shells[last_anchor_id]));
|
||||
append(anchor_areas, to_polygons(last_anchor_id < int32_t(shells.size()) ? shells[last_anchor_id] : sparse[last_anchor_id]));
|
||||
}
|
||||
// if (Points &polyline = it_bridge_anchor->path; polyline.size() >= 2) {
|
||||
// reserve_more_power_of_2(lines, polyline.size() - 1);
|
||||
@ -270,17 +287,18 @@ Surfaces expand_bridges_detect_orientations(
|
||||
lines = to_lines(diff_pl(to_polylines(bridge.expolygon), expand(anchor_areas, float(SCALED_EPSILON))));
|
||||
auto [bridging_dir, unsupported_dist] = detect_bridging_direction(lines, to_polygons(bridge.expolygon));
|
||||
bridge.angle = M_PI + std::atan2(bridging_dir.y(), bridging_dir.x());
|
||||
// #if 1
|
||||
// coordf_t stroke_width = scale_(0.06);
|
||||
// BoundingBox bbox = get_extents(initial);
|
||||
// bbox.offset(scale_(1.));
|
||||
// ::Slic3r::SVG
|
||||
// svg(debug_out_path(("bridge"+std::to_string(bridges[idx_last].bridge_angle)+"_"+std::to_string(this->layer()->bottom_z())).c_str()),
|
||||
// bbox);
|
||||
|
||||
// svg.draw(initial, "cyan");
|
||||
// svg.draw(to_lines(lower_layer->lslices), "green", stroke_width);
|
||||
// #endif
|
||||
#if 0
|
||||
coordf_t stroke_width = scale_(0.06);
|
||||
BoundingBox bbox = get_extents(anchor_areas);
|
||||
bbox.merge(get_extents(bridge.expolygon));
|
||||
bbox.offset(scale_(1.));
|
||||
::Slic3r::SVG
|
||||
svg(debug_out_path(("bridge" + std::to_string(bridge.angle) + "_" /* + std::to_string(this->layer()->bottom_z())*/).c_str()),
|
||||
bbox);
|
||||
svg.draw(bridge.expolygon, "cyan");
|
||||
svg.draw(lines, "green", stroke_width);
|
||||
svg.draw(anchor_areas, "red");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -322,8 +340,11 @@ Surfaces expand_bridges_detect_orientations(
|
||||
}
|
||||
}
|
||||
|
||||
// Clip the shells by the expanded bridges.
|
||||
shells = diff_ex(shells, out);
|
||||
// Clip by the expanded bridges.
|
||||
if (expanded_into_shells)
|
||||
shells = diff_ex(shells, out);
|
||||
if (expanded_into_sparse)
|
||||
sparse = diff_ex(sparse, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -332,23 +353,43 @@ Surfaces expand_bridges_detect_orientations(
|
||||
static Surfaces expand_merge_surfaces(
|
||||
Surfaces &surfaces,
|
||||
SurfaceType surface_type,
|
||||
ExPolygons &shells,
|
||||
const Algorithm::RegionExpansionParameters ¶ms,
|
||||
ExPolygons &shells,
|
||||
const Algorithm::RegionExpansionParameters &expansion_params_into_solid_infill,
|
||||
ExPolygons &sparse,
|
||||
const Algorithm::RegionExpansionParameters &expansion_params_into_sparse_infill,
|
||||
const float closing_radius,
|
||||
const double bridge_angle = -1.)
|
||||
{
|
||||
using namespace Slic3r::Algorithm;
|
||||
|
||||
double thickness;
|
||||
ExPolygons src = fill_surfaces_extract_expolygons(surfaces, {surface_type}, thickness);
|
||||
if (src.empty())
|
||||
return {};
|
||||
|
||||
std::vector<ExPolygon> expanded = expand_merge_expolygons(std::move(src), shells, params);
|
||||
std::vector<RegionExpansion> expansions = propagate_waves(src, shells, expansion_params_into_solid_infill);
|
||||
bool expanded_into_shells = !expansions.empty();
|
||||
bool expanded_into_sparse = false;
|
||||
{
|
||||
std::vector<RegionExpansion> expansions2 = propagate_waves(src, sparse, expansion_params_into_sparse_infill);
|
||||
if (! expansions2.empty()) {
|
||||
expanded_into_sparse = true;
|
||||
for (RegionExpansion &expansion : expansions2)
|
||||
expansion.boundary_id += uint32_t(shells.size());
|
||||
append(expansions, std::move(expansions2));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ExPolygon> expanded = merge_expansions_into_expolygons(std::move(src), std::move(expansions));
|
||||
//NOTE: The current regularization of the shells can create small unasigned regions in the object (E.G. benchy)
|
||||
// without the following closing operation, those regions will stay unfilled and cause small holes in the expanded surface.
|
||||
// look for narrow_ensure_vertical_wall_thickness_region_radius filter.
|
||||
expanded = closing_ex(expanded, closing_radius);
|
||||
// Trim the shells by the expanded expolygons.
|
||||
shells = diff_ex(shells, expanded);
|
||||
if (expanded_into_shells)
|
||||
shells = diff_ex(shells, expanded);
|
||||
if (expanded_into_sparse)
|
||||
sparse = diff_ex(sparse, expanded);
|
||||
|
||||
Surface templ{ surface_type, {} };
|
||||
templ.bridge_angle = bridge_angle;
|
||||
@ -361,20 +402,25 @@ static Surfaces expand_merge_surfaces(
|
||||
|
||||
void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered)
|
||||
{
|
||||
using namespace Slic3r::Algorithm;
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
export_region_fill_surfaces_to_svg_debug("4_process_external_surfaces-initial");
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
|
||||
// Width of the perimeters.
|
||||
float shell_width = 0;
|
||||
float expansion_min = 0;
|
||||
if (int num_perimeters = this->region().config().perimeters; num_perimeters > 0) {
|
||||
Flow external_perimeter_flow = this->flow(frExternalPerimeter);
|
||||
Flow perimeter_flow = this->flow(frPerimeter);
|
||||
shell_width += 0.5f * external_perimeter_flow.scaled_width() + external_perimeter_flow.scaled_spacing();
|
||||
shell_width = 0.5f * external_perimeter_flow.scaled_width() + external_perimeter_flow.scaled_spacing();
|
||||
shell_width += perimeter_flow.scaled_spacing() * (num_perimeters - 1);
|
||||
expansion_min = perimeter_flow.scaled_spacing();
|
||||
} else {
|
||||
// TODO: Maybe there is better solution when printing with zero perimeters, but this works reasonably well, given the situation
|
||||
shell_width = float(SCALED_EPSILON);
|
||||
shell_width = float(SCALED_EPSILON);
|
||||
expansion_min = float(SCALED_EPSILON);;
|
||||
}
|
||||
|
||||
// Scaled expansions of the respective external surfaces.
|
||||
@ -390,16 +436,18 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
||||
|
||||
// Expand the top / bottom / bridge surfaces into the shell thickness solid infills.
|
||||
double layer_thickness;
|
||||
ExPolygons shells = union_ex(fill_surfaces_extract_expolygons(m_fill_surfaces.surfaces, {stInternalSolid}, layer_thickness));
|
||||
ExPolygons shells = union_ex(fill_surfaces_extract_expolygons(m_fill_surfaces.surfaces, { stInternalSolid }, layer_thickness));
|
||||
ExPolygons sparse = union_ex(fill_surfaces_extract_expolygons(m_fill_surfaces.surfaces, { stInternal }, layer_thickness));
|
||||
|
||||
SurfaceCollection bridges;
|
||||
const auto expansion_params_into_sparse_infill = RegionExpansionParameters::build(expansion_min, expansion_step, max_nr_expansion_steps);
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges. layer" << this->layer()->print_z;
|
||||
const double custom_angle = this->region().config().bridge_angle.value;
|
||||
const auto params = Algorithm::RegionExpansionParameters::build(expansion_bottom_bridge, expansion_step, max_nr_expansion_steps);
|
||||
const auto expansion_params_into_solid_infill = RegionExpansionParameters::build(expansion_bottom_bridge, expansion_step, max_nr_expansion_steps);
|
||||
bridges.surfaces = custom_angle > 0 ?
|
||||
expand_merge_surfaces(m_fill_surfaces.surfaces, stBottomBridge, shells, params, closing_radius, Geometry::deg2rad(custom_angle)) :
|
||||
expand_bridges_detect_orientations(m_fill_surfaces.surfaces, shells, params, closing_radius);
|
||||
expand_merge_surfaces(m_fill_surfaces.surfaces, stBottomBridge, shells, expansion_params_into_solid_infill, sparse, expansion_params_into_sparse_infill, closing_radius, Geometry::deg2rad(custom_angle)) :
|
||||
expand_bridges_detect_orientations(m_fill_surfaces.surfaces, shells, expansion_params_into_solid_infill, sparse, expansion_params_into_sparse_infill, closing_radius);
|
||||
BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done";
|
||||
#if 0
|
||||
{
|
||||
@ -410,15 +458,25 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
||||
}
|
||||
|
||||
Surfaces bottoms = expand_merge_surfaces(m_fill_surfaces.surfaces, stBottom, shells,
|
||||
Algorithm::RegionExpansionParameters::build(expansion_bottom, expansion_step, max_nr_expansion_steps), closing_radius);
|
||||
RegionExpansionParameters::build(expansion_bottom, expansion_step, max_nr_expansion_steps),
|
||||
sparse, expansion_params_into_sparse_infill, closing_radius);
|
||||
Surfaces tops = expand_merge_surfaces(m_fill_surfaces.surfaces, stTop, shells,
|
||||
Algorithm::RegionExpansionParameters::build(expansion_top, expansion_step, max_nr_expansion_steps), closing_radius);
|
||||
RegionExpansionParameters::build(expansion_top, expansion_step, max_nr_expansion_steps),
|
||||
sparse, expansion_params_into_sparse_infill, closing_radius);
|
||||
|
||||
m_fill_surfaces.remove_types({ stBottomBridge, stBottom, stTop, stInternalSolid });
|
||||
reserve_more(m_fill_surfaces.surfaces, shells.size() + bridges.size() + bottoms.size() + tops.size());
|
||||
Surface solid_templ(stInternalSolid, {});
|
||||
solid_templ.thickness = layer_thickness;
|
||||
m_fill_surfaces.append(std::move(shells), solid_templ);
|
||||
// m_fill_surfaces.remove_types({ stBottomBridge, stBottom, stTop, stInternal, stInternalSolid });
|
||||
m_fill_surfaces.clear();
|
||||
reserve_more(m_fill_surfaces.surfaces, shells.size() + sparse.size() + bridges.size() + bottoms.size() + tops.size());
|
||||
{
|
||||
Surface solid_templ(stInternalSolid, {});
|
||||
solid_templ.thickness = layer_thickness;
|
||||
m_fill_surfaces.append(std::move(shells), solid_templ);
|
||||
}
|
||||
{
|
||||
Surface sparse_templ(stInternal, {});
|
||||
sparse_templ.thickness = layer_thickness;
|
||||
m_fill_surfaces.append(std::move(sparse), sparse_templ);
|
||||
}
|
||||
m_fill_surfaces.append(std::move(bridges.surfaces));
|
||||
m_fill_surfaces.append(std::move(bottoms));
|
||||
m_fill_surfaces.append(std::move(tops));
|
||||
|
@ -981,11 +981,12 @@ void PrintObject::detect_surfaces_type()
|
||||
surface_type_bottom_other);
|
||||
#else
|
||||
// Any surface lying on the void is a true bottom bridge (an overhang)
|
||||
ExPolygons true_bridge = diff_ex(layerm->slices().surfaces, lower_layer->lslices, ApplySafetyOffset::Yes);
|
||||
// expand the bridges by one extrusion width, to ensure reasonable anchoring whenever possible
|
||||
true_bridge = intersection_ex(layerm->slices().surfaces,
|
||||
offset_ex(true_bridge, layerm->bridging_flow(frSolidInfill).scaled_spacing()));
|
||||
surfaces_append(bottom, true_bridge, surface_type_bottom_other);
|
||||
surfaces_append(
|
||||
bottom,
|
||||
opening_ex(
|
||||
diff_ex(layerm->slices().surfaces, lower_layer->lslices, ApplySafetyOffset::Yes),
|
||||
offset),
|
||||
surface_type_bottom_other);
|
||||
// if user requested internal shells, we need to identify surfaces
|
||||
// lying on other slices not belonging to this region
|
||||
if (interface_shells) {
|
||||
|
Loading…
Reference in New Issue
Block a user