Better detection of bridge orientation.

Before, partial bridges were calculated. For each of these an angle
has been choosen and after that all the bridges have been merged.
Becouse of the merge a single angle had to be picked.
The first angle that the algorithm has stumbled upon has been picked,
resulting in sub-optimal bridging directions.

Now the partial bridges are calcualted, then merged and then
the angle is decided over the whole merged bridge.
This approach at least ensures that the orientation is the best possible
for the merged bridge.
This commit is contained in:
Martin Šach 2024-06-11 16:10:24 +02:00 committed by Lukas Matena
parent 1b36955ca2
commit a91a7d6b0e
2 changed files with 17 additions and 63 deletions

View File

@ -198,7 +198,6 @@ struct Bridge {
ExPolygon expolygon; ExPolygon expolygon;
uint32_t group_id; uint32_t group_id;
std::vector<Algorithm::RegionExpansionEx>::const_iterator bridge_expansion_begin; std::vector<Algorithm::RegionExpansionEx>::const_iterator bridge_expansion_begin;
std::optional<double> angle{std::nullopt};
}; };
// Group the bridge surfaces by overlaps. // Group the bridge surfaces by overlaps.
@ -282,54 +281,6 @@ std::vector<Bridge> get_grouped_bridges(
return result; return result;
} }
void detect_bridge_directions(
const Algorithm::WaveSeeds& bridge_anchors,
std::vector<Bridge>& bridges,
const std::vector<ExpansionZone>& expansion_zones
) {
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;
int32_t last_anchor_id = -1;
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);
unsigned start_index{};
unsigned end_index{};
for (const ExpansionZone& expansion_zone: expansion_zones) {
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();
}
}
}
Lines 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 constexpr (false) {
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");
}
}
}
Surfaces merge_bridges( Surfaces merge_bridges(
std::vector<Bridge>& bridges, std::vector<Bridge>& bridges,
const std::vector<Algorithm::RegionExpansionEx>& bridge_expansions, const std::vector<Algorithm::RegionExpansionEx>& bridge_expansions,
@ -345,29 +296,33 @@ Surfaces merge_bridges(
for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id) { for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id) {
if (group_id(bridges, bridge_id) == bridge_id) { if (group_id(bridges, bridge_id) == bridge_id) {
// Head of the group. // Head of the group.
Polygons acc; Polygons bridge_group;
for (uint32_t bridge_id2 = bridge_id; bridge_id2 < uint32_t(bridges.size()); ++ bridge_id2) Polygons expansions;
for (uint32_t bridge_id2 = bridge_id; bridge_id2 < uint32_t(bridges.size()); ++ bridge_id2) {
if (group_id(bridges, bridge_id2) == bridge_id) { if (group_id(bridges, bridge_id2) == bridge_id) {
append(acc, to_polygons(std::move(bridges[bridge_id2].expolygon))); append(bridge_group, to_polygons(std::move(bridges[bridge_id2].expolygon)));
auto it_bridge_expansion = bridges[bridge_id2].bridge_expansion_begin; auto it_bridge_expansion = bridges[bridge_id2].bridge_expansion_begin;
assert(it_bridge_expansion == bridge_expansions.end() || it_bridge_expansion->src_id == bridge_id2); 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) 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)); append(expansions, 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, {} }; append(bridge_group, expansions);
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) //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. // 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.
ExPolygons final = closing_ex(acc, closing_radius); ExPolygons merged_bridges = closing_ex(bridge_group, closing_radius);
// without safety offset, artifacts are generated (GH #2494) // without safety offset, artifacts are generated (GH #2494)
// union_safety_offset_ex(acc) // union_safety_offset_ex(acc)
for (ExPolygon &ex : final)
result.emplace_back(templ, std::move(ex)); for (ExPolygon &bridge_expolygon : merged_bridges) {
Surface surface{ stBottomBridge, std::move(bridge_expolygon) };
const Lines lines{to_lines(diff_pl(to_polylines(bridge_expolygon), expand(expansions, float(SCALED_EPSILON))))};
auto [bridging_dir, unsupported_dist] = detect_bridging_direction(lines, to_polygons(bridge_expolygon));
surface.bridge_angle = M_PI + std::atan2(bridging_dir.y(), bridging_dir.x());
result.push_back(std::move(surface));
}
} }
} }
return result; return result;
@ -444,7 +399,6 @@ Surfaces expand_bridges_detect_orientations(
bridge_expolygons.clear(); bridge_expolygons.clear();
std::sort(expansion_result.anchors.begin(), expansion_result.anchors.end(), Algorithm::lower_by_src_and_boundary); 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.
std::sort(expansion_result.expansions.begin(), expansion_result.expansions.end(), [](auto &l, auto &r) { std::sort(expansion_result.expansions.begin(), expansion_result.expansions.end(), [](auto &l, auto &r) {

View File

@ -131,7 +131,7 @@ TEST_CASE_METHOD(LayerRegionFixture, "test the bridge expansion with the bridge
} }
REQUIRE(result.size() == 2); REQUIRE(result.size() == 2);
CHECK(result.at(0).bridge_angle == Approx(1.5707963268)); CHECK(std::fmod(result.at(1).bridge_angle, M_PI) == Approx(0.0));
CHECK(std::fmod(result.at(1).bridge_angle, M_PI) == Approx(0.0)); CHECK(std::fmod(result.at(1).bridge_angle, M_PI) == Approx(0.0));
CHECK(result.at(0).expolygon.contour.size() == 22); CHECK(result.at(0).expolygon.contour.size() == 22);
CHECK(result.at(1).expolygon.contour.size() == 14); CHECK(result.at(1).expolygon.contour.size() == 14);