FIX: bridge not correctly anchored

1. Fix external bridge anchor problem

This commit is cherry-picked from Prusa.Thanks prusa
Original commit: 81d9724,74a38ed,4d0bae1

jira:NEW

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: If8e3b5902341d1777a156b2da8c8f21713683d31
This commit is contained in:
xun.zhang 2024-05-10 12:20:33 +08:00 committed by Lane.Wei
parent c5562423fd
commit eb76a8eb28
2 changed files with 318 additions and 190 deletions

View File

@ -6,6 +6,8 @@
#include "Flow.hpp" #include "Flow.hpp"
#include "SurfaceCollection.hpp" #include "SurfaceCollection.hpp"
#include "ExtrusionEntityCollection.hpp" #include "ExtrusionEntityCollection.hpp"
#include "RegionExpansion.hpp"
namespace Slic3r { namespace Slic3r {
@ -331,6 +333,36 @@ inline std::vector<float> zs_from_layers(const LayerContainer &layers)
extern BoundingBox get_extents(const LayerRegion &layer_region); extern BoundingBox get_extents(const LayerRegion &layer_region);
extern BoundingBox get_extents(const LayerRegionPtrs &layer_regions); extern BoundingBox get_extents(const LayerRegionPtrs &layer_regions);
struct ExpansionZone {
ExPolygons expolygons;
Algorithm::RegionExpansionParameters parameters;
bool expanded_into = false;
};
/**
* Extract bridging surfaces from "surfaces", expand them into "shells" using expansion_params,
* detect bridges.
* Trim "shells" by the expanded bridges.
*/
Surfaces expand_bridges_detect_orientations(
Surfaces &surfaces,
std::vector<ExpansionZone>& expansion_zones,
const float closing_radius
);
/**
* Extract bridging surfaces from "surfaces", expand them into "shells" using expansion_params.
* Trim "shells" by the expanded bridges.
*/
Surfaces expand_merge_surfaces(
Surfaces &surfaces,
SurfaceType surface_type,
std::vector<ExpansionZone>& expansion_zones,
const float closing_radius,
const double bridge_angle = -1
);
} }
#endif #endif

View File

@ -136,201 +136,279 @@ static ExPolygons fill_surfaces_extract_expolygons(Surfaces &surfaces, std::init
return out; return out;
} }
// Extract bridging surfaces from "surfaces", expand them into "shells" using expansion_params, // Cache for detecting bridge orientation and merging regions with overlapping expansions.
// detect bridges. struct Bridge {
// Trim "shells" by the expanded bridges. ExPolygon expolygon;
Surfaces expand_bridges_detect_orientations( uint32_t group_id;
Surfaces &surfaces, std::vector<Algorithm::RegionExpansionEx>::const_iterator bridge_expansion_begin;
ExPolygons &shells, std::optional<double> angle{std::nullopt};
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;
double thickness; // Group the bridge surfaces by overlaps.
ExPolygons bridges_ex = fill_surfaces_extract_expolygons(surfaces, {stBottomBridge}, thickness); uint32_t group_id(std::vector<Bridge> &bridges, uint32_t src_id) {
if (bridges_ex.empty()) uint32_t group_id = bridges[src_id].group_id;
return {}; while (group_id != src_id) {
src_id = group_id;
// Calculate bridge anchors and their expansions in their respective shell region. group_id = bridges[src_id].group_id;
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));
}
} }
bridges[src_id].group_id = group_id;
return group_id;
};
// Cache for detecting bridge orientation and merging regions with overlapping expansions. std::vector<Bridge> get_grouped_bridges(
struct Bridge { ExPolygons&& bridge_expolygons,
ExPolygon expolygon; const std::vector<Algorithm::RegionExpansionEx>& bridge_expansions
uint32_t group_id; ) {
std::vector<RegionExpansionEx>::const_iterator bridge_expansion_begin; using namespace Algorithm;
double angle = -1;
}; std::vector<Bridge> result;
std::vector<Bridge> bridges;
{ {
bridges.reserve(bridges_ex.size()); result.reserve(bridge_expansions.size());
uint32_t group_id = 0; uint32_t group_id = 0;
for (ExPolygon &ex : bridges_ex) using std::move_iterator;
bridges.push_back({ std::move(ex), group_id ++, bridge_expansions.end() }); for (ExPolygon& expolygon : bridge_expolygons)
bridges_ex.clear(); result.push_back({ std::move(expolygon), group_id ++, bridge_expansions.end() });
} }
// Group the bridge surfaces by overlaps.
auto group_id = [&bridges](uint32_t src_id) {
uint32_t group_id = bridges[src_id].group_id;
while (group_id != src_id) {
src_id = group_id;
group_id = bridges[src_id].group_id;
}
bridges[src_id].group_id = group_id;
return group_id;
};
{ // Detect overlaps of bridge anchors inside their respective shell regions.
// bridge_expansions are sorted by boundary id and source id.
for (auto expansion_iterator = bridge_expansions.begin(); expansion_iterator != bridge_expansions.end();) {
auto boundary_region_begin = expansion_iterator;
auto boundary_region_end = std::find_if(
next(expansion_iterator),
bridge_expansions.end(),
[&](const RegionExpansionEx& expansion){
return expansion.boundary_id != expansion_iterator->boundary_id;
}
);
// Cache of bboxes per expansion boundary. // Cache of bboxes per expansion boundary.
std::vector<BoundingBox> bboxes; std::vector<BoundingBox> bounding_boxes;
// Detect overlaps of bridge anchors inside their respective shell regions. bounding_boxes.reserve(std::distance(boundary_region_begin, boundary_region_end));
// bridge_expansions are sorted by boundary id and source id. std::transform(
for (auto it = bridge_expansions.begin(); it != bridge_expansions.end();) { boundary_region_begin,
// For each boundary region: boundary_region_end,
auto it_begin = it; std::back_inserter(bounding_boxes),
auto it_end = std::next(it_begin); [](const RegionExpansionEx& expansion){
for (; it_end != bridge_expansions.end() && it_end->boundary_id == it_begin->boundary_id; ++ it_end) ; return get_extents(expansion.expolygon.contour);
bboxes.clear(); }
bboxes.reserve(it_end - it_begin); );
for (auto it2 = it_begin; it2 != it_end; ++ it2)
bboxes.emplace_back(get_extents(it2->expolygon.contour)); // For each bridge anchor of the current source:
// For each bridge anchor of the current source: for (;expansion_iterator != boundary_region_end; ++expansion_iterator) {
for (; it != it_end; ++ it) { auto candidate_iterator = std::next(expansion_iterator);
// A grup id for this bridge. for (;candidate_iterator != boundary_region_end; ++candidate_iterator) {
for (auto it2 = std::next(it); it2 != it_end; ++ it2) const BoundingBox& current_bounding_box{
if (it->src_id != it2->src_id && bounding_boxes[expansion_iterator - boundary_region_begin]
bboxes[it - it_begin].overlap(bboxes[it2 - it_begin]) && };
// One may ignore holes, they are irrelevant for intersection test. const BoundingBox& candidate_bounding_box{
! intersection(it->expolygon.contour, it2->expolygon.contour).empty()) { bounding_boxes[candidate_iterator - boundary_region_begin]
// The two bridge regions intersect. Give them the same (lower) group id. };
uint32_t id = group_id(it->src_id); if (
uint32_t id2 = group_id(it2->src_id); expansion_iterator->src_id != candidate_iterator->src_id
if (id < id2) && current_bounding_box.overlap(candidate_bounding_box)
bridges[id2].group_id = id; // One may ignore holes, they are irrelevant for intersection test.
else && !intersection(expansion_iterator->expolygon.contour, candidate_iterator->expolygon.contour).empty()
bridges[id].group_id = id2; ) {
} // The two bridge regions intersect. Give them the same (lower) group id.
uint32_t id = group_id(result, expansion_iterator->src_id);
uint32_t id2 = group_id(result, candidate_iterator->src_id);
if (id < id2)
result[id2].group_id = id;
else
result[id].group_id = id2;
}
} }
} }
} }
return result;
}
// Detect bridge directions. void detect_bridge_directions(
{ const Algorithm::WaveSeeds& bridge_anchors,
std::sort(bridge_anchors.begin(), bridge_anchors.end(), Algorithm::lower_by_src_and_boundary); std::vector<Bridge>& bridges,
auto it_bridge_anchor = bridge_anchors.begin(); const std::vector<ExpansionZone>& expansion_zones
Lines lines; ) {
if (expansion_zones.empty()) {
throw std::runtime_error("At least one expansion zone must exist!");
}
auto it_bridge_anchor = bridge_anchors.begin();
for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id) {
Bridge &bridge = bridges[bridge_id];
Polygons anchor_areas; Polygons anchor_areas;
for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id) { int32_t last_anchor_id = -1;
Bridge &bridge = bridges[bridge_id]; for (; it_bridge_anchor != bridge_anchors.end() && it_bridge_anchor->src == bridge_id; ++ it_bridge_anchor) {
// lines.clear(); if (last_anchor_id != int(it_bridge_anchor->boundary)) {
anchor_areas.clear(); last_anchor_id = int(it_bridge_anchor->boundary);
int32_t last_anchor_id = -1;
for (; it_bridge_anchor != bridge_anchors.end() && it_bridge_anchor->src == bridge_id; ++ it_bridge_anchor) { unsigned start_index{};
if (last_anchor_id != int(it_bridge_anchor->boundary)) { unsigned end_index{};
last_anchor_id = int(it_bridge_anchor->boundary); for (const ExpansionZone& expansion_zone: expansion_zones) {
append(anchor_areas, to_polygons(last_anchor_id < int32_t(shells.size()) ? shells[last_anchor_id] : sparse[last_anchor_id - int32_t(shells.size())])); end_index += expansion_zone.expolygons.size();
if (last_anchor_id < static_cast<int64_t>(end_index)) {
append(anchor_areas, to_polygons(expansion_zone.expolygons[last_anchor_id - start_index]));
break;
}
start_index += expansion_zone.expolygons.size();
} }
// if (Points &polyline = it_bridge_anchor->path; polyline.size() >= 2) {
// reserve_more_power_of_2(lines, polyline.size() - 1);
// for (size_t i = 1; i < polyline.size(); ++ i)
// lines.push_back({ polyline[i - 1], polyline[1] });
// }
} }
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)); Lines lines{to_lines(diff_pl(to_polylines(bridge.expolygon), expand(anchor_areas, float(SCALED_EPSILON))))};
bridge.angle = M_PI + std::atan2(bridging_dir.y(), bridging_dir.x()); auto [bridging_dir, unsupported_dist] = detect_bridging_direction(lines, to_polygons(bridge.expolygon));
#if 0 bridge.angle = M_PI + std::atan2(bridging_dir.y(), bridging_dir.x());
if constexpr (false) {
coordf_t stroke_width = scale_(0.06); coordf_t stroke_width = scale_(0.06);
BoundingBox bbox = get_extents(anchor_areas); BoundingBox bbox = get_extents(anchor_areas);
bbox.merge(get_extents(bridge.expolygon)); bbox.merge(get_extents(bridge.expolygon));
bbox.offset(scale_(1.)); bbox.offset(scale_(1.));
::Slic3r::SVG ::Slic3r::SVG
svg(debug_out_path(("bridge" + std::to_string(bridge.angle) + "_" /* + std::to_string(this->layer()->bottom_z())*/).c_str()), svg(debug_out_path(("bridge" + std::to_string(*bridge.angle) + "_" /* + std::to_string(this->layer()->bottom_z())*/).c_str()),
bbox); bbox);
svg.draw(bridge.expolygon, "cyan"); svg.draw(bridge.expolygon, "cyan");
svg.draw(lines, "green", stroke_width); svg.draw(lines, "green", stroke_width);
svg.draw(anchor_areas, "red"); svg.draw(anchor_areas, "red");
#endif
} }
} }
}
Surfaces merge_bridges(
std::vector<Bridge>& bridges,
const std::vector<Algorithm::RegionExpansionEx>& bridge_expansions,
const float closing_radius
) {
for (auto it = bridge_expansions.begin(); it != bridge_expansions.end(); ) {
bridges[it->src_id].bridge_expansion_begin = it;
uint32_t src_id = it->src_id;
for (++ it; it != bridge_expansions.end() && it->src_id == src_id; ++ it) ;
}
Surfaces result;
for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id) {
if (group_id(bridges, bridge_id) == bridge_id) {
// Head of the group.
Polygons acc;
for (uint32_t bridge_id2 = bridge_id; bridge_id2 < uint32_t(bridges.size()); ++ bridge_id2)
if (group_id(bridges, bridge_id2) == bridge_id) {
append(acc, to_polygons(std::move(bridges[bridge_id2].expolygon)));
auto it_bridge_expansion = bridges[bridge_id2].bridge_expansion_begin;
assert(it_bridge_expansion == bridge_expansions.end() || it_bridge_expansion->src_id == bridge_id2);
for (; it_bridge_expansion != bridge_expansions.end() && it_bridge_expansion->src_id == bridge_id2; ++ it_bridge_expansion)
append(acc, to_polygons(it_bridge_expansion->expolygon));
}
//FIXME try to be smart and pick the best bridging angle for all?
if (!bridges[bridge_id].angle) {
assert(false && "Bridge angle must be pre-calculated!");
}
Surface templ{ stBottomBridge, {} };
templ.bridge_angle = bridges[bridge_id].angle ? *bridges[bridge_id].angle : -1;
//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.
ExPolygons final = closing_ex(acc, closing_radius);
// without safety offset, artifacts are generated (GH #2494)
// union_safety_offset_ex(acc)
for (ExPolygon &ex : final)
result.emplace_back(templ, std::move(ex));
}
}
return result;
}
struct ExpansionResult {
Algorithm::WaveSeeds anchors;
std::vector<Algorithm::RegionExpansionEx> expansions;
};
ExpansionResult expand_expolygons(
const ExPolygons& expolygons,
std::vector<ExpansionZone>& expansion_zones
) {
using namespace Algorithm;
WaveSeeds bridge_anchors;
std::vector<RegionExpansionEx> bridge_expansions;
unsigned processed_bridges_count = 0;
for (ExpansionZone& expansion_zone : expansion_zones) {
WaveSeeds seeds{wave_seeds(
expolygons,
expansion_zone.expolygons,
expansion_zone.parameters.tiny_expansion,
true
)};
std::vector<RegionExpansionEx> expansions{propagate_waves_ex(
seeds,
expansion_zone.expolygons,
expansion_zone.parameters
)};
for (WaveSeed &seed : seeds)
seed.boundary += processed_bridges_count;
for (RegionExpansionEx &expansion : expansions)
expansion.boundary_id += processed_bridges_count;
expansion_zone.expanded_into = ! expansions.empty();
append(bridge_anchors, std::move(seeds));
append(bridge_expansions, std::move(expansions));
processed_bridges_count += expansion_zone.expolygons.size();
}
return {bridge_anchors, bridge_expansions};
}
// Extract bridging surfaces from "surfaces", expand them into "shells" using expansion_params,
// detect bridges.
// Trim "shells" by the expanded bridges.
Surfaces expand_bridges_detect_orientations(
Surfaces &surfaces,
std::vector<ExpansionZone>& expansion_zones,
const float closing_radius
)
{
using namespace Slic3r::Algorithm;
double thickness;
ExPolygons bridge_expolygons = fill_surfaces_extract_expolygons(surfaces, {stBottomBridge}, thickness);
if (bridge_expolygons.empty())
return {};
// Calculate bridge anchors and their expansions in their respective shell region.
ExpansionResult expansion_result{expand_expolygons(
bridge_expolygons,
expansion_zones
)};
std::vector<Bridge> bridges{get_grouped_bridges(
std::move(bridge_expolygons),
expansion_result.expansions
)};
bridge_expolygons.clear();
std::sort(expansion_result.anchors.begin(), expansion_result.anchors.end(), Algorithm::lower_by_src_and_boundary);
detect_bridge_directions(expansion_result.anchors, bridges, expansion_zones);
// Merge the groups with the same group id, produce surfaces by merging source overhangs with their newly expanded anchors. // Merge the groups with the same group id, produce surfaces by merging source overhangs with their newly expanded anchors.
Surfaces out; std::sort(expansion_result.expansions.begin(), expansion_result.expansions.end(), [](auto &l, auto &r) {
{ return l.src_id < r.src_id || (l.src_id == r.src_id && l.boundary_id < r.boundary_id);
Polygons acc; });
Surface templ{ stBottomBridge, {} }; Surfaces out{merge_bridges(bridges, expansion_result.expansions, closing_radius)};
std::sort(bridge_expansions.begin(), bridge_expansions.end(), [](auto &l, auto &r) {
return l.src_id < r.src_id || (l.src_id == r.src_id && l.boundary_id < r.boundary_id);
});
for (auto it = bridge_expansions.begin(); it != bridge_expansions.end(); ) {
bridges[it->src_id].bridge_expansion_begin = it;
uint32_t src_id = it->src_id;
for (++ it; it != bridge_expansions.end() && it->src_id == src_id; ++ it) ;
}
for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id)
if (group_id(bridge_id) == bridge_id) {
// Head of the group.
acc.clear();
for (uint32_t bridge_id2 = bridge_id; bridge_id2 < uint32_t(bridges.size()); ++ bridge_id2)
if (group_id(bridge_id2) == bridge_id) {
append(acc, to_polygons(std::move(bridges[bridge_id2].expolygon)));
auto it_bridge_expansion = bridges[bridge_id2].bridge_expansion_begin;
assert(it_bridge_expansion == bridge_expansions.end() || it_bridge_expansion->src_id == bridge_id2);
for (; it_bridge_expansion != bridge_expansions.end() && it_bridge_expansion->src_id == bridge_id2; ++ it_bridge_expansion)
append(acc, to_polygons(std::move(it_bridge_expansion->expolygon)));
}
//FIXME try to be smart and pick the best bridging angle for all?
templ.bridge_angle = bridges[bridge_id].angle;
//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.
ExPolygons final = closing_ex(acc, closing_radius);
// without safety offset, artifacts are generated (GH #2494)
// union_safety_offset_ex(acc)
for (ExPolygon &ex : final)
out.emplace_back(templ, std::move(ex));
}
}
// Clip by the expanded bridges. // Clip by the expanded bridges.
if (expanded_into_shells) for (ExpansionZone& expansion_zone : expansion_zones)
shells = diff_ex(shells, out); if (expansion_zone.expanded_into)
if (expanded_into_sparse) expansion_zone.expolygons = diff_ex(expansion_zone.expolygons, out);
sparse = diff_ex(sparse, out);
return out; return out;
} }
// Extract bridging surfaces from "surfaces", expand them into "shells" using expansion_params. Surfaces expand_merge_surfaces(
// Trim "shells" by the expanded bridges. Surfaces &surfaces,
static Surfaces expand_merge_surfaces( SurfaceType surface_type,
Surfaces &surfaces, std::vector<ExpansionZone>& expansion_zones,
SurfaceType surface_type, const float closing_radius,
ExPolygons &shells, const double bridge_angle
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; using namespace Slic3r::Algorithm;
@ -339,17 +417,17 @@ static Surfaces expand_merge_surfaces(
if (src.empty()) if (src.empty())
return {}; return {};
std::vector<RegionExpansion> expansions = propagate_waves(src, shells, expansion_params_into_solid_infill); unsigned processed_expolygons_count = 0;
bool expanded_into_shells = !expansions.empty(); std::vector<RegionExpansion> expansions;
bool expanded_into_sparse = false; for (ExpansionZone& expansion_zone : expansion_zones) {
{ std::vector<RegionExpansion> zone_expansions = propagate_waves(src, expansion_zone.expolygons, expansion_zone.parameters);
std::vector<RegionExpansion> expansions2 = propagate_waves(src, sparse, expansion_params_into_sparse_infill); expansion_zone.expanded_into = !zone_expansions.empty();
if (! expansions2.empty()) {
expanded_into_sparse = true; for (RegionExpansion &expansion : zone_expansions)
for (RegionExpansion &expansion : expansions2) expansion.boundary_id += processed_expolygons_count;
expansion.boundary_id += uint32_t(shells.size());
append(expansions, std::move(expansions2)); processed_expolygons_count += expansion_zone.expolygons.size();
} append(expansions, std::move(zone_expansions));
} }
std::vector<ExPolygon> expanded = merge_expansions_into_expolygons(std::move(src), std::move(expansions)); std::vector<ExPolygon> expanded = merge_expansions_into_expolygons(std::move(src), std::move(expansions));
@ -357,11 +435,10 @@ static Surfaces expand_merge_surfaces(
// without the following closing operation, those regions will stay unfilled and cause small holes in the expanded surface. // 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. // look for narrow_ensure_vertical_wall_thickness_region_radius filter.
expanded = closing_ex(expanded, closing_radius); expanded = closing_ex(expanded, closing_radius);
// Trim the shells by the expanded expolygons. // Trim the zones by the expanded expolygons.
if (expanded_into_shells) for (ExpansionZone& expansion_zone : expansion_zones)
shells = diff_ex(shells, expanded); if (expansion_zone.expanded_into)
if (expanded_into_sparse) expansion_zone.expolygons = diff_ex(expansion_zone.expolygons, expanded);
sparse = diff_ex(sparse, expanded);
Surface templ{ surface_type, {} }; Surface templ{ surface_type, {} };
templ.bridge_angle = bridge_angle; templ.bridge_angle = bridge_angle;
@ -410,16 +487,23 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
double layer_thickness; double layer_thickness;
ExPolygons shells = union_ex(fill_surfaces_extract_expolygons(fill_surfaces.surfaces, { stInternalSolid }, layer_thickness)); ExPolygons shells = union_ex(fill_surfaces_extract_expolygons(fill_surfaces.surfaces, { stInternalSolid }, layer_thickness));
ExPolygons sparse = union_ex(fill_surfaces_extract_expolygons(fill_surfaces.surfaces, { stInternal }, layer_thickness)); ExPolygons sparse = union_ex(fill_surfaces_extract_expolygons(fill_surfaces.surfaces, { stInternal }, layer_thickness));
ExPolygons top_expolygons = union_ex(fill_surfaces_extract_expolygons(fill_surfaces.surfaces, { stTop }, layer_thickness));
const auto expansion_params_into_sparse_infill = RegionExpansionParameters::build(expansion_min, expansion_step, max_nr_expansion_steps);
const auto expansion_params_into_solid_infill = RegionExpansionParameters::build(expansion_bottom_bridge, expansion_step, max_nr_expansion_steps);
std::vector<ExpansionZone> expansion_zones{
ExpansionZone{std::move(shells), expansion_params_into_solid_infill},
ExpansionZone{std::move(sparse), expansion_params_into_sparse_infill},
ExpansionZone{std::move(top_expolygons), expansion_params_into_solid_infill},
};
SurfaceCollection bridges; 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; 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 double custom_angle = this->region().config().bridge_angle.value;
const auto expansion_params_into_solid_infill = RegionExpansionParameters::build(expansion_bottom_bridge, expansion_step, max_nr_expansion_steps);
bridges.surfaces = custom_angle > 0 ? bridges.surfaces = custom_angle > 0 ?
expand_merge_surfaces(fill_surfaces.surfaces, stBottomBridge, shells, expansion_params_into_solid_infill, sparse, expansion_params_into_sparse_infill, closing_radius, Geometry::deg2rad(custom_angle)) : expand_merge_surfaces(fill_surfaces.surfaces, stBottomBridge, expansion_zones, closing_radius, Geometry::deg2rad(custom_angle)) :
expand_bridges_detect_orientations(fill_surfaces.surfaces, shells, expansion_params_into_solid_infill, sparse, expansion_params_into_sparse_infill, closing_radius); expand_bridges_detect_orientations(fill_surfaces.surfaces, expansion_zones, closing_radius);
BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done"; BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done";
#if 0 #if 0
{ {
@ -429,25 +513,36 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
#endif #endif
} }
Surfaces bottoms = expand_merge_surfaces(fill_surfaces.surfaces, stBottom, shells, fill_surfaces.remove_types({ stTop });
RegionExpansionParameters::build(expansion_bottom, expansion_step, max_nr_expansion_steps), {
sparse, expansion_params_into_sparse_infill, closing_radius); Surface top_templ(stTop, {});
Surfaces tops = expand_merge_surfaces(fill_surfaces.surfaces, stTop, shells, top_templ.thickness = layer_thickness;
RegionExpansionParameters::build(expansion_top, expansion_step, max_nr_expansion_steps), fill_surfaces.append(std::move(expansion_zones.back().expolygons), top_templ);
sparse, expansion_params_into_sparse_infill, closing_radius); }
expansion_zones.pop_back();
expansion_zones.at(0).parameters = RegionExpansionParameters::build(expansion_bottom, expansion_step, max_nr_expansion_steps);
Surfaces bottoms = expand_merge_surfaces(fill_surfaces.surfaces, stBottom, expansion_zones, closing_radius);
expansion_zones.at(0).parameters = RegionExpansionParameters::build(expansion_top, expansion_step, max_nr_expansion_steps);
Surfaces tops = expand_merge_surfaces(fill_surfaces.surfaces, stTop, expansion_zones, closing_radius);
// m_fill_surfaces.remove_types({ stBottomBridge, stBottom, stTop, stInternal, stInternalSolid }); // m_fill_surfaces.remove_types({ stBottomBridge, stBottom, stTop, stInternal, stInternalSolid });
fill_surfaces.clear(); fill_surfaces.clear();
reserve_more(fill_surfaces.surfaces, shells.size() + sparse.size() + bridges.size() + bottoms.size() + tops.size()); unsigned zones_expolygons_count = 0;
for (const ExpansionZone& zone : expansion_zones)
zones_expolygons_count += zone.expolygons.size();
reserve_more(fill_surfaces.surfaces, zones_expolygons_count + bridges.size() + bottoms.size() + tops.size());
{ {
Surface solid_templ(stInternalSolid, {}); Surface solid_templ(stInternalSolid, {});
solid_templ.thickness = layer_thickness; solid_templ.thickness = layer_thickness;
fill_surfaces.append(std::move(shells), solid_templ); fill_surfaces.append(std::move(expansion_zones[0].expolygons), solid_templ);
} }
{ {
Surface sparse_templ(stInternal, {}); Surface sparse_templ(stInternal, {});
sparse_templ.thickness = layer_thickness; sparse_templ.thickness = layer_thickness;
fill_surfaces.append(std::move(sparse), sparse_templ); fill_surfaces.append(std::move(expansion_zones[1].expolygons), sparse_templ);
} }
fill_surfaces.append(std::move(bridges.surfaces)); fill_surfaces.append(std::move(bridges.surfaces));
fill_surfaces.append(std::move(bottoms)); fill_surfaces.append(std::move(bottoms));
@ -457,6 +552,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
export_region_fill_surfaces_to_svg_debug("4_process_external_surfaces-final"); export_region_fill_surfaces_to_svg_debug("4_process_external_surfaces-final");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
} }
#else #else
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3. //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.