diff --git a/src/libslic3r/Algorithm/RegionExpansion.cpp b/src/libslic3r/Algorithm/RegionExpansion.cpp index 4cd4689e6..83561fc83 100644 --- a/src/libslic3r/Algorithm/RegionExpansion.cpp +++ b/src/libslic3r/Algorithm/RegionExpansion.cpp @@ -153,6 +153,46 @@ static inline void merge_splits(ClipperLib_Z::Paths &paths, std::vector; + +static AABBTreeBBoxes build_aabb_tree_over_expolygons(const ExPolygons &expolygons) +{ + // Calculate bounding boxes of internal slices. + std::vector bboxes; + bboxes.reserve(expolygons.size()); + for (size_t i = 0; i < expolygons.size(); ++ i) + bboxes.emplace_back(i, get_extents(expolygons[i].contour)); + // Build AABB tree over bounding boxes of boundary expolygons. + AABBTreeBBoxes out; + out.build_modify_input(bboxes); + return out; +} + +static int sample_in_expolygons( + // AABB tree over boundary expolygons + const AABBTreeBBoxes &aabb_tree, + const ExPolygons &expolygons, + const Point &sample) +{ + int out = -1; + AABBTreeIndirect::traverse(aabb_tree, + [&sample](const AABBTreeBBoxes::Node &node) { + return node.bbox.contains(sample); + }, + [&expolygons, &sample, &out](const AABBTreeBBoxes::Node &node) { + assert(node.is_leaf()); + assert(node.is_valid()); + if (expolygons[node.idx].contains(sample)) { + out = int(node.idx); + // Stop traversal. + return false; + } + // Continue traversal. + return true; + }); + return out; +} + std::vector wave_seeds( // Source regions that are supposed to touch the boundary. const ExPolygons &src, @@ -211,19 +251,7 @@ std::vector wave_seeds( // AABBTree over bounding boxes of boundaries. // Only built if necessary, that is if any of the seed contours is closed, thus there is no intersection point // with the boundary and all Z coordinates of the closed contour point to the source contour. - using AABBTree = AABBTreeIndirect::Tree<2, coord_t>; - AABBTree aabb_tree; - auto init_aabb_tree = [&aabb_tree, &boundary]() { - if (aabb_tree.empty()) { - // Calculate bounding boxes of internal slices. - std::vector bboxes; - bboxes.reserve(boundary.size()); - for (size_t i = 0; i < boundary.size(); ++ i) - bboxes.emplace_back(i, get_extents(boundary[i].contour)); - // Build AABB tree over bounding boxes of boundary expolygons. - aabb_tree.build_modify_input(bboxes); - } - }; + AABBTreeBBoxes aabb_tree; // Sort paths into their respective islands. // Each src x boundary will be processed (wave expanded) independently. @@ -262,24 +290,9 @@ std::vector wave_seeds( // This should be a closed contour. assert(front == back && front.z() >= idx_boundary_end && front.z() < idx_src_end); // Find a source boundary expolygon of one sample of this closed path. - init_aabb_tree(); - Point sample(front.x(), front.y()); - int boundary_id = -1; - AABBTreeIndirect::traverse(aabb_tree, - [&sample](const AABBTree::Node &node) { - return node.bbox.contains(sample); - }, - [&boundary, &sample, &boundary_id](const AABBTree::Node &node) { - assert(node.is_leaf()); - assert(node.is_valid()); - if (boundary[node.idx].contains(sample)) { - boundary_id = int(node.idx); - // Stop traversal. - return false; - } - // Continue traversal. - return true; - }); + if (aabb_tree.empty()) + aabb_tree = build_aabb_tree_over_expolygons(boundary); + int boundary_id = sample_in_expolygons(aabb_tree, boundary, Point(front.x(), front.y())); // Boundary that contains the sample point was found. assert(boundary_id >= 0); if (boundary_id >= 0) @@ -431,6 +444,7 @@ std::vector propagate_waves_ex(const WaveSeeds &seeds, const for (ExPolygon &ex : expolys) out.push_back({ std::move(ex), it->src_id, it->boundary_id }); } + it = it2; } return out; } @@ -477,21 +491,44 @@ std::vector expand_merge_expolygons(ExPolygons &&src, const ExPolygon ExPolygons out; out.reserve(src.size()); for (auto it = expanded.begin(); it != expanded.end();) { - auto it2 = it; - acc.clear(); - for (; it2 != expanded.end() && it->src_id == it2->src_id; ++ it2) - acc.emplace_back(std::move(it2->polygon)); for (; last < it->src_id; ++ last) out.emplace_back(std::move(src[last])); + acc.clear(); + assert(it->src_id == last); + for (; it != expanded.end() && it->src_id == last; ++ it) + acc.emplace_back(std::move(it->polygon)); //FIXME offset & merging could be more efficient, for example one does not need to copy the source expolygon - append(acc, to_polygons(std::move(src[it->src_id]))); + ExPolygon &src_ex = src[last ++]; + assert(! src_ex.contour.empty()); +#if 0 + { + static int iRun = 0; + BoundingBox bbox = get_extents(acc); + bbox.merge(get_extents(src_ex)); + SVG svg(debug_out_path("expand_merge_expolygons-failed-union=%d.svg", iRun ++).c_str(), bbox); + svg.draw(acc); + svg.draw_outline(acc, "black", scale_(0.05)); + svg.draw(src_ex, "red"); + svg.Close(); + } +#endif + Point sample = src_ex.contour.front(); + append(acc, to_polygons(std::move(src_ex))); ExPolygons merged = union_safety_offset_ex(acc); // Expanding one expolygon by waves should not change connectivity of the source expolygon: // Single expolygon should be produced possibly with increased number of holes. - assert(merged.size() == 1); - if (! merged.empty()) + if (merged.size() > 1) { + // assert(merged.size() == 1); + // There is something wrong with the initial waves. Most likely the bridge was not valid at all + // or the boundary region was very close to some bridge edge, but not really touching. + // Pick only a single merged expolygon, which contains one sample point of the source expolygon. + auto aabb_tree = build_aabb_tree_over_expolygons(merged); + int id = sample_in_expolygons(aabb_tree, merged, sample); + assert(id != -1); + if (id != -1) + out.emplace_back(std::move(merged[id])); + } else if (merged.size() == 1) out.emplace_back(std::move(merged.front())); - it = it2; } for (; last < uint32_t(src.size()); ++ last) out.emplace_back(std::move(src[last])); diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index d28b64bff..8413095ce 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -183,16 +183,17 @@ Surfaces expand_bridges_detect_orientations( // Cache for detecting bridge orientation and merging regions with overlapping expansions. struct Bridge { - ExPolygon expolygon; - uint32_t group_id; - double angle = -1; + ExPolygon expolygon; + uint32_t group_id; + std::vector::const_iterator bridge_expansion_begin; + double angle = -1; }; std::vector bridges; { bridges.reserve(bridges_ex.size()); uint32_t group_id = 0; for (ExPolygon &ex : bridges_ex) - bridges.push_back({ std::move(ex), group_id ++ }); + bridges.push_back({ std::move(ex), group_id ++, bridge_expansions.end() }); bridges_ex.clear(); } @@ -243,15 +244,24 @@ Surfaces expand_bridges_detect_orientations( std::sort(bridge_anchors.begin(), bridge_anchors.end(), Algorithm::lower_by_src_and_boundary); auto it_bridge_anchor = bridge_anchors.begin(); Lines lines; + Polygons anchor_areas; for (uint32_t bridge_id = 0; bridge_id < uint32_t(bridges.size()); ++ bridge_id) { Bridge &bridge = bridges[bridge_id]; - lines.clear(); - for (++ it_bridge_anchor; it_bridge_anchor != bridge_anchors.end() && it_bridge_anchor->src == bridge_id; ++ it_bridge_anchor) - 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.clear(); + anchor_areas.clear(); + 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); + append(anchor_areas, std::move(to_polygons(shells[last_anchor_id]))); } +// 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)); bridge.angle = M_PI + std::atan2(bridging_dir.y(), bridging_dir.x()); // #if 1 @@ -273,12 +283,23 @@ Surfaces expand_bridges_detect_orientations( { Polygons acc; Surface templ{ stBottomBridge, {} }; + 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) { acc.clear(); for (uint32_t bridge_id2 = bridge_id; bridge_id2 < uint32_t(bridges.size()); ++ bridge_id2) if (group_id(bridge_id) == bridge_id) { append(acc, to_polygons(std::move(bridges[bridge_id2].expolygon))); - append(acc, to_polygons(std::move(bridge_expansions[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; @@ -355,8 +376,8 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly 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); bridges.surfaces = custom_angle > 0 ? - expand_bridges_detect_orientations(m_fill_surfaces.surfaces, shells, params) : - expand_merge_surfaces(m_fill_surfaces.surfaces, stBottomBridge, shells, params, custom_angle); + expand_merge_surfaces(m_fill_surfaces.surfaces, stBottomBridge, shells, params, custom_angle) : + expand_bridges_detect_orientations(m_fill_surfaces.surfaces, shells, params); BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done"; #if 0 {