From b0a8757b12b02b97a3142851449e9d6e00927536 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 13 Mar 2023 16:31:48 +0100 Subject: [PATCH 01/17] CutGizmo: Use isApprox() instead of "==" operator for Vec3d values --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 3ba67e60e..e9b6fa694 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -799,7 +799,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers() void GLGizmoCut3D::render_cut_line() { - if (!cut_line_processing() || m_line_end == Vec3d::Zero()) + if (!cut_line_processing() || m_line_end.isApprox(Vec3d::Zero())) return; glsafe(::glEnable(GL_DEPTH_TEST)); @@ -1130,7 +1130,7 @@ void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data) rotation[m_hover_id] = theta; const Transform3d rotation_tmp = m_start_dragging_m * rotation_transform(rotation); - const bool update_tbb = m_rotation_m.rotation() != rotation_tmp.rotation(); + const bool update_tbb = !m_rotation_m.rotation().isApprox(rotation_tmp.rotation()); m_rotation_m = rotation_tmp; if (update_tbb) m_transformed_bounding_box = transformed_bounding_box(m_plane_center, m_rotation_m); @@ -1262,7 +1262,7 @@ void GLGizmoCut3D::update_bb() const BoundingBoxf3 box = bounding_box(); if (!box.defined) return; - if (m_max_pos != box.max || m_min_pos != box.min) { + if (!m_max_pos.isApprox(box.max) || !m_min_pos.isApprox(box.min)) { m_bounding_box = box; @@ -1679,7 +1679,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) const bool has_connectors = !connectors.empty(); - const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && m_bb_center == m_plane_center; + const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && m_bb_center.isApprox(m_plane_center); m_imgui->disabled_begin(is_cut_plane_init); wxString act_name = _L("Reset cutting plane"); if (render_reset_button("cut_plane", into_u8(act_name))) { @@ -2247,7 +2247,7 @@ void GLGizmoCut3D::update_connector_shape() bool GLGizmoCut3D::cut_line_processing() const { - return m_line_beg != Vec3d::Zero(); + return !m_line_beg.isApprox(Vec3d::Zero()); } void GLGizmoCut3D::discard_cut_line_processing() From 993d6bd561baf57ad2e36c247a39759e2742b666 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 14 Mar 2023 11:11:51 +0100 Subject: [PATCH 02/17] Fixed a hang in tree supports, caused by integer overflow on coord_t. Fixes #10048 --- src/libslic3r/TreeSupport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 2ce404161..240bcf7d1 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -1451,7 +1451,7 @@ static void generate_initial_areas( // As a circle is round this length is identical for every axis as long as the 90 degrees angle between both remains. const coord_t circle_length_to_half_linewidth_change = config.min_radius < config.support_line_width ? config.min_radius / 2 : - sqrt(sqr(config.min_radius) - sqr(config.min_radius - config.support_line_width / 2)); + scale_(sqrt(sqr(unscale(config.min_radius)) - sqr(unscale(config.min_radius - config.support_line_width / 2)))); // Extra support offset to compensate for larger tip radiis. Also outset a bit more when z overwrites xy, because supporting something with a part of a support line is better than not supporting it at all. //FIXME Vojtech: This is not sufficient for support enforcers to work. //FIXME There is no account for the support overhang angle. From f4e44f975069bcf08531d0d15e9b793f85d5ea41 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 8 Mar 2023 18:42:27 +0100 Subject: [PATCH 03/17] rework of bridiging over sparse infill in progress --- src/libslic3r/Fill/Fill.cpp | 17 +-- src/libslic3r/Layer.hpp | 7 +- src/libslic3r/Print.hpp | 6 +- src/libslic3r/PrintObject.cpp | 194 ++++++++++++++++++++++++++++++---- 4 files changed, 187 insertions(+), 37 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 02f613c15..8ca0199d0 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -486,14 +486,6 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: size_t first_object_layer_id = this->object()->get_layer(0)->id(); for (SurfaceFill &surface_fill : surface_fills) { - //skip patterns for which additional input is nullptr - switch (surface_fill.params.pattern) { - case ipLightning: if (lightning_generator == nullptr) continue; break; - case ipAdaptiveCubic: if (adaptive_fill_octree == nullptr) continue; break; - case ipSupportCubic: if (support_fill_octree == nullptr) continue; break; - default: break; - } - // Create the filler object. std::unique_ptr f = std::unique_ptr(Fill::new_from_type(surface_fill.params.pattern)); f->set_bounding_box(bbox); @@ -647,7 +639,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: #endif } -Polylines Layer::generate_sparse_infill_polylines_for_anchoring() const +Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree) const { std::vector surface_fills = group_fills(*this); const Slic3r::BoundingBox bbox = this->object()->bounding_box(); @@ -656,14 +648,13 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring() const Polylines sparse_infill_polylines{}; for (SurfaceFill &surface_fill : surface_fills) { - // skip patterns for which additional input is nullptr switch (surface_fill.params.pattern) { case ipLightning: continue; break; - case ipAdaptiveCubic: continue; break; - case ipSupportCubic: continue; break; case ipCount: continue; break; case ipSupportBase: continue; break; case ipEnsuring: continue; break; + case ipAdaptiveCubic: + case ipSupportCubic: case ipRectilinear: case ipMonotonic: case ipMonotonicLines: @@ -688,7 +679,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring() const f->layer_id = this->id(); f->z = this->print_z; f->angle = surface_fill.params.angle; - // f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree; + f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree; f->print_config = &this->object()->print()->config(); f->print_object_config = &this->object()->config(); diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index a59c029b8..0cba55d3c 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -368,8 +368,11 @@ public: void make_perimeters(); // Phony version of make_fills() without parameters for Perl integration only. void make_fills() { this->make_fills(nullptr, nullptr, nullptr); } - void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator); - Polylines generate_sparse_infill_polylines_for_anchoring() const; + void make_fills(FillAdaptive::Octree *adaptive_fill_octree, + FillAdaptive::Octree *support_fill_octree, + FillLightning::Generator *lightning_generator); + Polylines generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree *adaptive_fill_octree, + FillAdaptive::Octree *support_fill_octree) const; void make_ironing(); void export_region_slices_to_svg(const char *path) const; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 3795c2449..df2bde469 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_Print_hpp_ #define slic3r_Print_hpp_ +#include "Fill/FillAdaptive.hpp" #include "PrintBase.hpp" #include "BoundingBox.hpp" @@ -385,7 +386,8 @@ private: void discover_horizontal_shells(); void combine_infill(); void _generate_support_material(); - std::pair prepare_adaptive_infill_data(); + std::pair prepare_adaptive_infill_data( + const std::vector>& surfaces_w_bottom_z) const; FillLightning::GeneratorPtr prepare_lightning_infill_data(); // XYZ in scaled coordinates @@ -410,6 +412,8 @@ private: // this is set to true when LayerRegion->slices is split in top/internal/bottom // so that next call to make_perimeters() performs a union() before computing loops bool m_typed_slices = false; + + std::pair adaptive_fill_octrees; }; struct WipeTowerData diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 4ddb36b9e..33029aac5 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -37,7 +37,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -400,7 +402,8 @@ void PrintObject::infill() if (this->set_started(posInfill)) { m_print->set_status(45, L("making infill")); - auto [adaptive_fill_octree, support_fill_octree] = this->prepare_adaptive_infill_data(); + const auto& adaptive_fill_octree = this->adaptive_fill_octrees.first; + const auto& support_fill_octree = this->adaptive_fill_octrees.second; auto lightning_generator = this->prepare_lightning_infill_data(); BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; @@ -509,7 +512,8 @@ void PrintObject::estimate_curled_extrusions() } } -std::pair PrintObject::prepare_adaptive_infill_data() +std::pair PrintObject::prepare_adaptive_infill_data( + const std::vector> &surfaces_w_bottom_z) const { using namespace FillAdaptive; @@ -523,22 +527,18 @@ std::pair PrintObject::prepare its_transform(mesh, to_octree * this->trafo_centered(), true); // Triangulate internal bridging surfaces. - std::vector> overhangs(this->layers().size()); - tbb::parallel_for( - tbb::blocked_range(0, int(m_layers.size()) - 1), - [this, &to_octree, &overhangs](const tbb::blocked_range &range) { - std::vector &out = overhangs[range.begin()]; - for (int idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - m_print->throw_if_canceled(); - const Layer *layer = this->layers()[idx_layer]; - for (const LayerRegion *layerm : layer->regions()) - for (const Surface &surface : layerm->fill_surfaces()) - if (surface.surface_type == stInternalBridge) - append(out, triangulate_expolygon_3d(surface.expolygon, layer->bottom_z())); - } - for (Vec3d &p : out) - p = (to_octree * p).eval(); - }); + std::vector> overhangs(surfaces_w_bottom_z.size()); + tbb::parallel_for(tbb::blocked_range(0, surfaces_w_bottom_z.size()), + [this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range &range) { + for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) { + std::vector &out = overhangs[surface_idx]; + m_print->throw_if_canceled(); + append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon, + surfaces_w_bottom_z[surface_idx].second)); + for (Vec3d &p : out) + p = (to_octree * p).eval(); + } + }); // and gather them. for (size_t i = 1; i < overhangs.size(); ++ i) append(overhangs.front(), std::move(overhangs[i])); @@ -1582,9 +1582,9 @@ void PrintObject::bridge_over_infill() { BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Start" << log_memory_info(); - struct ModifiedSurface + struct CandidateSurface { - ModifiedSurface(const Surface *original_surface, Polygons new_polys, const LayerRegion *region, double bridge_angle) + CandidateSurface(const Surface *original_surface, Polygons new_polys, const LayerRegion *region, double bridge_angle) : original_surface(original_surface), new_polys(new_polys), region(region), bridge_angle(bridge_angle) {} const Surface *original_surface; @@ -1593,9 +1593,161 @@ void PrintObject::bridge_over_infill() double bridge_angle; }; + tbb::concurrent_vector candidate_surfaces; + + tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = static_cast(this), + &candidate_surfaces](tbb::blocked_range r) { + for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { + const Layer *layer = po->get_layer(lidx); + if (layer->lower_layer == nullptr) { + continue; + } + auto spacing = layer->regions().front()->flow(frSolidInfill, true).scaled_spacing(); + Polygons internal_area = shrink(to_polygons(layer->lower_layer->lslices), 4 * spacing); + Polygons lower_layer_solids; + for (const LayerRegion *region : layer->lower_layer->regions()) { + bool has_low_density = region->region().config().fill_density.value < 100; + for (const Surface &surface : region->fill_surfaces()) { + if (surface.surface_type == stInternal && has_low_density) { + Polygons p = to_polygons(surface.expolygon); + lower_layer_solids.insert(lower_layer_solids.end(), p.begin(), p.end()); + } + } + } + lower_layer_solids = expand(lower_layer_solids, 4 * spacing); + internal_area = diff(internal_area, lower_layer_solids); + + for (const LayerRegion *region : layer->regions()) { + SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); + for (const Surface *s : region_internal_solids) { + Polygons away_from_perimeter = intersection(to_polygons(s->expolygon), internal_area); + if (!away_from_perimeter.empty()) { + Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(away_from_perimeter, 5 * spacing)); + candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0)); + } + } + } + } + }); + + std::map> surfaces_by_layer; + std::vector> surfaces_w_bottom_z; + for (const CandidateSurface& c : candidate_surfaces) { + surfaces_by_layer[c.region->layer()->id()].push_back(c); + surfaces_w_bottom_z.emplace_back(c.original_surface, c.region->m_layer->bottom_z()); + } + + this->adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_bottom_z); + + std::map infill_lines; + std::vector layers_to_generate_infill; + for (const auto& pair : surfaces_by_layer) { + assert(pair.first > 0); + infill_lines[pair.first-1] = {}; + layers_to_generate_infill.push_back(pair.first-1); + } + + tbb::parallel_for(tbb::blocked_range(0, layers_to_generate_infill.size()), [po = static_cast(this), + &infill_lines](tbb::blocked_range r) { + for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { + infill_lines.at( + lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->adaptive_fill_octrees.first.get(), + po->adaptive_fill_octrees.second.get()); + } + }); + + std::vector> jobs; + for (auto pair : surfaces_by_layer) { + if (jobs.empty() || jobs.back().second < pair.first) { + jobs.emplace_back(pair.first, pair.first + 1); + } else { + jobs.back().second = pair.first + 1; + } + } + + auto gahter_lower_layers_sparse_infill = [](const PrintObject *po, int lidx, float target_flow_height) { + // Gather lower layers sparse infill areas, to depth defined by used bridge flow + Polygons lower_layers_sparse_infill{}; + Polygons special_infill{}; + Polygons not_sparse_infill{}; + double bottom_z = po->get_layer(lidx)->print_z - target_flow_height - EPSILON; + for (int i = int(lidx) - 1; i >= 0; --i) { + // Stop iterating if layer is lower than bottom_z. + if (po->get_layer(i)->print_z < bottom_z) + break; + for (const auto &link : current_links) { + const LayerSlice &slice_below = po->get_layer(i)->lslices_ex[link.slice_idx]; + next_links.insert(next_links.end(), slice_below.overlaps_below.begin(), slice_below.overlaps_below.end()); + std::unordered_set regions_under_to_check; + for (const LayerIsland &island : slice_below.islands) { + regions_under_to_check.insert(po->get_layer(i)->regions()[island.perimeters.region()]); + if (!island.fill_expolygons_composite()) { + regions_under_to_check.insert(po->get_layer(i)->regions()[island.fill_region_id]); + } else { + for (const auto &r : po->get_layer(i)->regions()) { + regions_under_to_check.insert(r); + } + break; + } + } + + for (const LayerRegion *region : regions_under_to_check) { + bool has_low_density = region->region().config().fill_density.value < 100; + bool has_special_infill = region_has_special_infill(region); + for (const Surface &surface : region->fill_surfaces()) { + if (surface.surface_type == stInternal && has_low_density && !has_special_infill) { + Polygons p = to_polygons(surface.expolygon); + lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), p.begin(), p.end()); + } else if (surface.surface_type == stInternal && has_low_density && has_special_infill) { + Polygons p = to_polygons(surface.expolygon); + special_infill.insert(special_infill.end(), p.begin(), p.end()); + } else { + Polygons p = to_polygons(surface.expolygon); + not_sparse_infill.insert(not_sparse_infill.end(), p.begin(), p.end()); + } + } + } + } + current_links = next_links; + next_links.clear(); + } + + lower_layers_sparse_infill = intersection(lower_layers_sparse_infill, + layer->lslices[int(candidates.first - layer->lslices_ex.data())]); + lower_layers_sparse_infill = diff(lower_layers_sparse_infill, not_sparse_infill); + special_infill = intersection(special_infill, layer->lslices[int(candidates.first - layer->lslices_ex.data())]); + special_infill = diff(special_infill, not_sparse_infill); + + lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), special_infill.begin(), special_infill.end()); + + if (shrink(lower_layers_sparse_infill, 3.0 * scale_(max_bridge_flow_height[candidates.first])).empty()) { + continue; + } + }; + + tbb::parallel_for(tbb::blocked_range(0, jobs.size()), [po = this, &jobs, &surfaces_by_layer](tbb::blocked_range r) { + for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { + for (size_t lidx = jobs[job_idx].first; lidx < jobs[job_idx].second; lidx++) { + const Layer *layer = po->get_layer(lidx); + + // Presort the candidate polygons. This will help choose the same angle for neighbournig surfaces, that would otherwise + // compete over anchoring sparse infill lines, leaving one area unachored + std::sort(surfaces_by_layer[lidx].begin(), surfaces_by_layer[lidx].end(), [](const Surface* left, const Surface* right){ + auto a = get_extents(left->expolygon); + auto b = get_extents(right->expolygon); + + if (a.min.x() == b.min.x()) { + return a.min.y() < b.min.y(); + }; + return a.min.x() < b.min.x(); + }); + } + } + }); + std::unordered_map> bridging_surfaces; - tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = static_cast(this), + tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = this, &bridging_surfaces](tbb::blocked_range r) { for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { const Layer *layer = po->get_layer(lidx); From f8e7d1b01c114b4d45f9e221c6b5bb935065d650 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 9 Mar 2023 16:17:08 +0100 Subject: [PATCH 04/17] core implemented, now fixing the issues --- src/libslic3r/Fill/Fill.cpp | 6 +- src/libslic3r/PrintObject.cpp | 956 +++++++++++++--------------------- 2 files changed, 380 insertions(+), 582 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 8ca0199d0..a6b420607 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -16,6 +16,7 @@ #include "FillLightning.hpp" #include "FillConcentric.hpp" #include "FillEnsuring.hpp" +#include "Polygon.hpp" namespace Slic3r { @@ -649,7 +650,10 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc for (SurfaceFill &surface_fill : surface_fills) { switch (surface_fill.params.pattern) { - case ipLightning: continue; break; + case ipLightning: { + auto polylines = to_polylines(shrink_ex(surface_fill.expolygons, 5.0 * surface_fill.params.flow.scaled_spacing())); + sparse_infill_polylines.insert(sparse_infill_polylines.end(), polylines.begin(), polylines.end()); + }; break; case ipCount: continue; break; case ipSupportBase: continue; break; case ipEnsuring: continue; break; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 33029aac5..42e652437 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -16,6 +16,7 @@ #include "Layer.hpp" #include "MutablePolygon.hpp" #include "PrintBase.hpp" +#include "PrintConfig.hpp" #include "SupportMaterial.hpp" #include "TreeSupport.hpp" #include "Surface.hpp" @@ -1648,669 +1649,462 @@ void PrintObject::bridge_over_infill() } tbb::parallel_for(tbb::blocked_range(0, layers_to_generate_infill.size()), [po = static_cast(this), + &layers_to_generate_infill, &infill_lines](tbb::blocked_range r) { - for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { + for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { + size_t lidx = layers_to_generate_infill[job_idx]; infill_lines.at( lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->adaptive_fill_octrees.first.get(), po->adaptive_fill_octrees.second.get()); } }); - std::vector> jobs; + // cluster layers by depth needed for thick bridges. Each cluster is to be processed by single thread sequentially, so that bridges cannot appear one on another + std::vector> clustered_layers_for_threads; for (auto pair : surfaces_by_layer) { - if (jobs.empty() || jobs.back().second < pair.first) { - jobs.emplace_back(pair.first, pair.first + 1); + if (clustered_layers_for_threads.empty() || this->get_layer(clustered_layers_for_threads.back().back())->print_z > + this->get_layer(pair.first)->print_z - + this->get_layer(pair.first)->regions()[0]->flow(frSolidInfill, true).height() - + EPSILON) { + clustered_layers_for_threads.push_back({pair.first}); } else { - jobs.back().second = pair.first + 1; + clustered_layers_for_threads.back().push_back(pair.first); } } - auto gahter_lower_layers_sparse_infill = [](const PrintObject *po, int lidx, float target_flow_height) { - // Gather lower layers sparse infill areas, to depth defined by used bridge flow - Polygons lower_layers_sparse_infill{}; - Polygons special_infill{}; - Polygons not_sparse_infill{}; - double bottom_z = po->get_layer(lidx)->print_z - target_flow_height - EPSILON; - for (int i = int(lidx) - 1; i >= 0; --i) { - // Stop iterating if layer is lower than bottom_z. - if (po->get_layer(i)->print_z < bottom_z) - break; - for (const auto &link : current_links) { - const LayerSlice &slice_below = po->get_layer(i)->lslices_ex[link.slice_idx]; - next_links.insert(next_links.end(), slice_below.overlaps_below.begin(), slice_below.overlaps_below.end()); - std::unordered_set regions_under_to_check; - for (const LayerIsland &island : slice_below.islands) { - regions_under_to_check.insert(po->get_layer(i)->regions()[island.perimeters.region()]); - if (!island.fill_expolygons_composite()) { - regions_under_to_check.insert(po->get_layer(i)->regions()[island.fill_region_id]); - } else { - for (const auto &r : po->get_layer(i)->regions()) { - regions_under_to_check.insert(r); - } - break; - } - } + // LAMBDA to gather areas with sparse infill deep enough that we can fit thick bridges there. + auto gather_areas_w_depth = + [](const PrintObject *po, int lidx, float target_flow_height) { + // Gather lower layers sparse infill areas, to depth defined by used bridge flow + Polygons lower_layers_sparse_infill{}; + Polygons not_sparse_infill{}; + double bottom_z = po->get_layer(lidx)->print_z - target_flow_height - EPSILON; + for (int i = int(lidx) - 1; i >= 0; --i) { + // Stop iterating if layer is lower than bottom_z. + const Layer *layer = po->get_layer(i); + if (layer->print_z < bottom_z) + break; - for (const LayerRegion *region : regions_under_to_check) { - bool has_low_density = region->region().config().fill_density.value < 100; - bool has_special_infill = region_has_special_infill(region); + for (const LayerRegion *region : layer->regions()) { + bool has_low_density = region->region().config().fill_density.value < 100; for (const Surface &surface : region->fill_surfaces()) { - if (surface.surface_type == stInternal && has_low_density && !has_special_infill) { + if (surface.surface_type == stInternal && has_low_density) { Polygons p = to_polygons(surface.expolygon); lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), p.begin(), p.end()); - } else if (surface.surface_type == stInternal && has_low_density && has_special_infill) { - Polygons p = to_polygons(surface.expolygon); - special_infill.insert(special_infill.end(), p.begin(), p.end()); } else { Polygons p = to_polygons(surface.expolygon); not_sparse_infill.insert(not_sparse_infill.end(), p.begin(), p.end()); } } } + lower_layers_sparse_infill = union_(lower_layers_sparse_infill); + } + + return diff(lower_layers_sparse_infill, not_sparse_infill); + }; + + // LAMBDA do determine optimal bridging angle + auto determine_bridging_angle = [](const Polygons &bridged_area, const Lines &anchors, InfillPattern dominant_pattern) { + AABBTreeLines::LinesDistancer lines_tree(anchors); + + std::map counted_directions; + for (const Polygon &p : bridged_area) { + for (int point_idx = 0; point_idx < int(p.points.size()) - 1; ++point_idx) { + Vec2d start = p.points[point_idx].cast(); + Vec2d next = p.points[point_idx + 1].cast(); + Vec2d v = next - start; // vector from next to current + double dist_to_next = v.norm(); + v.normalize(); + int lines_count = int(std::ceil(dist_to_next / scaled(3.0))); + float step_size = dist_to_next / lines_count; + for (int i = 0; i < lines_count; ++i) { + Point a = (start + v * (i * step_size)).cast(); + auto [distance, index, p] = lines_tree.distance_from_lines_extra(a); + double angle = lines_tree.get_line(index).orientation(); + if (angle > PI) { + angle -= PI; + } + angle += PI * 0.5; + counted_directions[angle]++; + } } - current_links = next_links; - next_links.clear(); } - lower_layers_sparse_infill = intersection(lower_layers_sparse_infill, - layer->lslices[int(candidates.first - layer->lslices_ex.data())]); - lower_layers_sparse_infill = diff(lower_layers_sparse_infill, not_sparse_infill); - special_infill = intersection(special_infill, layer->lslices[int(candidates.first - layer->lslices_ex.data())]); - special_infill = diff(special_infill, not_sparse_infill); + std::pair best_dir{0, 0}; + // sliding window accumulation + for (const auto &dir : counted_directions) { + int score_acc = 0; + double dir_acc = 0; + double window_start_angle = dir.first - PI * 0.1; + double window_end_angle = dir.first + PI * 0.1; + for (auto dirs_window = counted_directions.lower_bound(window_start_angle); + dirs_window != counted_directions.upper_bound(window_end_angle); dirs_window++) { + dir_acc += dirs_window->first * dirs_window->second; + score_acc += dirs_window->second; + } + // current span of directions is 0.5 PI to 1.5 PI (due to the aproach.). Edge values should also account for the + // opposite direction. + if (window_start_angle < 0.5 * PI) { + for (auto dirs_window = counted_directions.lower_bound(1.5 * PI - (0.5 * PI - window_start_angle)); + dirs_window != counted_directions.end(); dirs_window++) { + dir_acc += dirs_window->first * dirs_window->second; + score_acc += dirs_window->second; + } + } + if (window_start_angle > 1.5 * PI) { + for (auto dirs_window = counted_directions.begin(); + dirs_window != counted_directions.upper_bound(window_start_angle - 1.5 * PI); dirs_window++) { + dir_acc += dirs_window->first * dirs_window->second; + score_acc += dirs_window->second; + } + } - lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), special_infill.begin(), special_infill.end()); - - if (shrink(lower_layers_sparse_infill, 3.0 * scale_(max_bridge_flow_height[candidates.first])).empty()) { - continue; + if (score_acc > best_dir.second) { + best_dir = {dir_acc / score_acc, score_acc}; + } } + double bridging_angle = best_dir.first; + if (bridging_angle == 0) { + bridging_angle = 0.001; + } + switch (dominant_pattern) { + case ipHilbertCurve: bridging_angle += 0.25 * PI; break; + case ipOctagramSpiral: bridging_angle += (1.0 / 16.0) * PI; break; + default: break; + } + + return bridging_angle; }; - tbb::parallel_for(tbb::blocked_range(0, jobs.size()), [po = this, &jobs, &surfaces_by_layer](tbb::blocked_range r) { - for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { - for (size_t lidx = jobs[job_idx].first; lidx < jobs[job_idx].second; lidx++) { - const Layer *layer = po->get_layer(lidx); - - // Presort the candidate polygons. This will help choose the same angle for neighbournig surfaces, that would otherwise - // compete over anchoring sparse infill lines, leaving one area unachored - std::sort(surfaces_by_layer[lidx].begin(), surfaces_by_layer[lidx].end(), [](const Surface* left, const Surface* right){ - auto a = get_extents(left->expolygon); - auto b = get_extents(right->expolygon); - - if (a.min.x() == b.min.x()) { - return a.min.y() < b.min.y(); - }; - return a.min.x() < b.min.x(); - }); + // LAMBDA that will fill given polygons with lines, exapand the lines to the nearest anchor, and reconstruct polygons from the newly + // generated lines + auto construct_anchored_polygon = [](Polygons bridged_area, Lines anchors, const Flow &bridging_flow, double bridging_angle) { + auto lines_rotate = [](Lines &lines, double cos_angle, double sin_angle) { + for (Line &l : lines) { + double ax = double(l.a.x()); + double ay = double(l.a.y()); + l.a.x() = coord_t(round(cos_angle * ax - sin_angle * ay)); + l.a.y() = coord_t(round(cos_angle * ay + sin_angle * ax)); + double bx = double(l.b.x()); + double by = double(l.b.y()); + l.b.x() = coord_t(round(cos_angle * bx - sin_angle * by)); + l.b.y() = coord_t(round(cos_angle * by + sin_angle * bx)); } - } - }); + }; - std::unordered_map> bridging_surfaces; + auto segments_overlap = [](coord_t alow, coord_t ahigh, coord_t blow, coord_t bhigh) { + return (alow >= blow && alow <= bhigh) || (ahigh >= blow && ahigh <= bhigh) || (blow >= alow && blow <= ahigh) || + (bhigh >= alow && bhigh <= ahigh); + }; - tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = this, - &bridging_surfaces](tbb::blocked_range r) { - for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { - const Layer *layer = po->get_layer(lidx); + Polygons expanded_bridged_area{}; + double aligning_angle = -bridging_angle + PI * 0.5; + { + polygons_rotate(bridged_area, aligning_angle); + lines_rotate(anchors, cos(aligning_angle), sin(aligning_angle)); + BoundingBox bb_x = get_extents(bridged_area); + BoundingBox bb_y = get_extents(anchors); - // gather also sparse infill surfaces on this layer, to which we can expand the bridges for anchoring - // gather potential internal bridging surfaces for the current layer - // pair of LayerSlice idx and surfaces. The LayerSlice idx simplifies the processing, since we cannot expand beyond it - std::unordered_map bridging_surface_candidates; - std::unordered_map expansion_space; - std::unordered_map max_bridge_flow_height; - std::unordered_map surface_to_region; - for (const LayerSlice &slice : layer->lslices_ex) { - AABBTreeLines::LinesDistancer slice_island_tree{to_lines(layer->lslices[int(&slice - layer->lslices_ex.data())])}; - std::unordered_set regions_to_check; + const size_t n_vlines = (bb_x.max.x() - bb_x.min.x() + bridging_flow.scaled_spacing() - 1) / bridging_flow.scaled_spacing(); + std::vector vertical_lines(n_vlines); + for (size_t i = 0; i < n_vlines; i++) { + coord_t x = bb_x.min.x() + i * bridging_flow.scaled_spacing(); + coord_t y_min = bb_y.min.y() - bridging_flow.scaled_spacing(); + coord_t y_max = bb_y.max.y() + bridging_flow.scaled_spacing(); + vertical_lines[i].a = Point{x, y_min}; + vertical_lines[i].b = Point{x, y_max}; + } - // If there is composite island we have to check all regions on the layer. otherwise, only some regions are needed to be checked - for (const LayerIsland &island : slice.islands) { - regions_to_check.insert(layer->regions()[island.perimeters.region()]); - if (!island.fill_expolygons_composite()) { - regions_to_check.insert(layer->regions()[island.fill_region_id]); + auto anchors_and_walls_tree = AABBTreeLines::LinesDistancer{std::move(anchors)}; + auto bridged_area_tree = AABBTreeLines::LinesDistancer{to_lines(bridged_area)}; + +#ifdef DEBUG_BRIDGE_OVER_INFILL + debug_draw(std::to_string(lidx) + "sliced", to_lines(bridged_area), anchors_and_walls, vertical_lines, {}); +#endif + + std::vector> polygon_sections(n_vlines); + for (size_t i = 0; i < n_vlines; i++) { + auto area_intersections = bridged_area_tree.intersections_with_line(vertical_lines[i]); + for (int intersection_idx = 0; intersection_idx < int(area_intersections.size()) - 1; intersection_idx++) { + if (bridged_area_tree.outside( + (area_intersections[intersection_idx].first + area_intersections[intersection_idx + 1].first) / 2) < 0) { + polygon_sections[i].emplace_back(area_intersections[intersection_idx].first, + area_intersections[intersection_idx + 1].first); + } + } + auto anchors_intersections = anchors_and_walls_tree.intersections_with_line(vertical_lines[i]); + + for (Line §ion : polygon_sections[i]) { + auto maybe_below_anchor = std::upper_bound(anchors_intersections.rbegin(), anchors_intersections.rend(), section.a, + [](const Point &a, const std::pair &b) { + return a.y() > b.first.y(); + }); + if (maybe_below_anchor != anchors_intersections.rend()) { + section.a = maybe_below_anchor->first; + section.a.y() -= bridging_flow.scaled_width() * (0.5 + 1.0); + } + + auto maybe_upper_anchor = std::upper_bound(anchors_intersections.begin(), anchors_intersections.end(), section.b, + [](const Point &a, const std::pair &b) { + return a.y() < b.first.y(); + }); + if (maybe_upper_anchor != anchors_intersections.end()) { + section.b = maybe_upper_anchor->first; + section.b.y() += bridging_flow.scaled_width() * (0.5 + 1.0); + } + } + + for (int section_idx = 0; section_idx < int(polygon_sections[i].size()) - 1; section_idx++) { + Line §ion_a = polygon_sections[i][section_idx]; + Line §ion_b = polygon_sections[i][section_idx + 1]; + if (segments_overlap(section_a.a.y(), section_a.b.y(), section_b.a.y(), section_b.b.y())) { + section_b.a = section_a.a.y() < section_b.a.y() ? section_a.a : section_b.a; + section_b.b = section_a.b.y() < section_b.b.y() ? section_b.b : section_a.b; + section_a.a = section_a.b; + } + } + + polygon_sections[i].erase(std::remove_if(polygon_sections[i].begin(), polygon_sections[i].end(), + [](const Line &s) { return s.a == s.b; }), + polygon_sections[i].end()); + } + + // reconstruct polygon from polygon sections + struct TracedPoly + { + std::vector lows; + std::vector highs; + }; + + std::vector current_traced_polys; + for (const auto &polygon_slice : polygon_sections) { + std::unordered_set used_segments; + for (TracedPoly &traced_poly : current_traced_polys) { + auto maybe_first_overlap = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(), + [](const Point &low, const Line &seg) { return seg.b.y() > low.y(); }); + + if (maybe_first_overlap != polygon_slice.end() && // segment exists + segments_overlap(traced_poly.lows.back().y(), traced_poly.highs.back().y(), maybe_first_overlap->a.y(), + maybe_first_overlap->b.y())) // segment is overlapping + { + // Overlapping segment. In that case, add it + // to the traced polygon and add segment to used segments + if ((traced_poly.lows.back() - maybe_first_overlap->a).cast().squaredNorm() < + 36.0 * double(bridging_flow.scaled_spacing()) * bridging_flow.scaled_spacing()) { + traced_poly.lows.push_back(maybe_first_overlap->a); + } else { + traced_poly.lows.push_back(traced_poly.lows.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); + traced_poly.lows.push_back(maybe_first_overlap->a - Point{bridging_flow.scaled_spacing() / 2, 0}); + traced_poly.lows.push_back(maybe_first_overlap->a); + } + + if ((traced_poly.highs.back() - maybe_first_overlap->b).cast().squaredNorm() < + 36.0 * double(bridging_flow.scaled_spacing()) * bridging_flow.scaled_spacing()) { + traced_poly.highs.push_back(maybe_first_overlap->b); + } else { + traced_poly.highs.push_back(traced_poly.highs.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); + traced_poly.highs.push_back(maybe_first_overlap->b - Point{bridging_flow.scaled_spacing() / 2, 0}); + traced_poly.highs.push_back(maybe_first_overlap->b); + } + used_segments.insert(&(*maybe_first_overlap)); } else { - for (const auto& r : layer->regions()) { - regions_to_check.insert(r); - } - break; + // Zero or multiple overlapping segments. Resolving this is nontrivial, + // so we just close this polygon and maybe open several new. This will hopefully happen much less often + traced_poly.lows.push_back(traced_poly.lows.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); + traced_poly.highs.push_back(traced_poly.highs.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); + Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows)); + new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); + traced_poly.lows.clear(); + traced_poly.highs.clear(); } } - for ( const LayerRegion *region : regions_to_check) { - SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); + current_traced_polys.erase(std::remove_if(current_traced_polys.begin(), current_traced_polys.end(), + [](const TracedPoly &tp) { return tp.lows.empty(); }), + current_traced_polys.end()); - // filter out surfaces not from this island... TODO sotre this info in the Z-Graph, so that this filtering is not needed - // NOTE: we are keeping even very small internal ensuring overhangs here. The aim is to later differentiate between expanding wall ensuring regions - // where briding them would be conterproductive, and small ensuring islands that expand into large ones, where bridging is quite necessary - region_internal_solids.erase(std::remove_if(region_internal_solids.begin(), region_internal_solids.end(), - [slice_island_tree](const Surface *s) { - if (slice_island_tree.outside(s->expolygon.contour.first_point()) > 0) { - return true; - } - return false; - }), - region_internal_solids.end()); - if (!region_internal_solids.empty()) { - max_bridge_flow_height[&slice] = std::max(max_bridge_flow_height[&slice], - region->bridging_flow(frSolidInfill, true).height()); + for (const auto &segment : polygon_slice) { + if (used_segments.find(&segment) == used_segments.end()) { + TracedPoly &new_tp = current_traced_polys.emplace_back(); + new_tp.lows.push_back(segment.a - Point{bridging_flow.scaled_spacing() / 2, 0}); + new_tp.lows.push_back(segment.a); + new_tp.highs.push_back(segment.b - Point{bridging_flow.scaled_spacing() / 2, 0}); + new_tp.highs.push_back(segment.b); } - for (const Surface *s : region_internal_solids) { - surface_to_region[s] = region; - } - bridging_surface_candidates[&slice].insert(bridging_surface_candidates[&slice].end(), region_internal_solids.begin(), - region_internal_solids.end()); - auto region_sparse_infill = region->fill_surfaces().filter_by_type(stInternal); - expansion_space[&slice].insert(expansion_space[&slice].end(), region_sparse_infill.begin(), region_sparse_infill.end()); } } - // if there are none briding candidates, exit now, before making infill for the previous layer - if (std::all_of(bridging_surface_candidates.begin(), bridging_surface_candidates.end(), - [](const std::pair &candidates) { return candidates.second.empty(); })) { - continue; + // add not closed polys + for (TracedPoly &traced_poly : current_traced_polys) { + Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows)); + new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); } - // generate sparse infill polylines from lower layers to get anchorable polylines - Polylines lower_layer_polylines = po->get_layer(lidx)->lower_layer - ? po->get_layer(lidx)->lower_layer->generate_sparse_infill_polylines_for_anchoring() - : Polylines(); +#ifdef DEBUG_BRIDGE_OVER_INFILL + Lines l{}; + for (const auto &s : polygon_sections) { + l.insert(l.end(), s.begin(), s.end()); + } + debug_draw(std::to_string(lidx) + "reconstructed", l, anchors_and_walls_tree.get_lines(), to_lines(expanded_bridged_area), + bridged_area_tree.get_lines()); +#endif + } - for (std::pair candidates : bridging_surface_candidates) { - if (candidates.second.empty()) { - continue; - }; + polygons_rotate(expanded_bridged_area, -aligning_angle); + return expanded_bridged_area; + }; - auto region_has_special_infill = [](const LayerRegion *layer_region) { - switch (layer_region->region().config().fill_pattern.value) { - case ipAdaptiveCubic: return true; - case ipSupportCubic: return true; - case ipLightning: return true; - default: return false; - } - }; + tbb::parallel_for(tbb::blocked_range(0, clustered_layers_for_threads.size()), [po = this, &surfaces_by_layer, + &clustered_layers_for_threads, + &gather_areas_w_depth, + &infill_lines, + &determine_bridging_angle, + &construct_anchored_polygon] + (tbb::blocked_range r) { + for (size_t cluster_idx = r.begin(); cluster_idx < r.end(); cluster_idx++) { + for (size_t job_idx = 0; job_idx < clustered_layers_for_threads[cluster_idx].size(); job_idx++) { + size_t lidx = clustered_layers_for_threads[cluster_idx][job_idx]; + const Layer *layer = po->get_layer(lidx); + // this thread has exclusive access to all surfaces in layers enumerated in + // clustered_layers_for_threads[cluster_idx] - // Gather lower layers sparse infill areas, to depth defined by used bridge flow - Polygons lower_layers_sparse_infill{}; - Polygons special_infill{}; - Polygons not_sparse_infill{}; - { - double bottom_z = layer->print_z - max_bridge_flow_height[candidates.first] - EPSILON; - std::vector current_links{}; - current_links.insert(current_links.end(), candidates.first->overlaps_below.begin(), - candidates.first->overlaps_below.end()); - std::vector next_links{}; - for (int i = int(lidx) - 1; i >= 0; --i) { - // Stop iterating if layer is lower than bottom_z. - if (po->get_layer(i)->print_z < bottom_z) + // Presort the candidate polygons. This will help choose the same angle for neighbournig surfaces, that + // would otherwise compete over anchoring sparse infill lines, leaving one area unachored + std::sort(surfaces_by_layer[lidx].begin(), surfaces_by_layer[lidx].end(), + [](const CandidateSurface &left, const CandidateSurface &right) { + auto a = get_extents(left.new_polys); + auto b = get_extents(right.new_polys); + + if (a.min.x() == b.min.x()) { + return a.min.y() < b.min.y(); + }; + return a.min.x() < b.min.x(); + }); + + // Gather deep infill areas, where thick bridges fit + coordf_t thick_bridges_depth = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).height(); + Polygons deep_infill_area = gather_areas_w_depth(po, lidx, thick_bridges_depth); + + // Now also remove area that has been already filled on lower layers by bridging expansion - For this + // reason we did the clustering of layers per thread. + double bottom_z = po->get_layer(lidx)->print_z - thick_bridges_depth - EPSILON; + if (job_idx > 0) { + for (int lower_job_idx = job_idx; lower_job_idx >= 0; lower_job_idx--) { + size_t lower_layer_idx = clustered_layers_for_threads[cluster_idx][lower_job_idx]; + const Layer *lower_layer = po->get_layer(lower_layer_idx); + if (lower_layer->print_z >= bottom_z) { + for (const auto &c : surfaces_by_layer[lower_layer_idx]) { + deep_infill_area = diff(deep_infill_area, c.new_polys); + } + } else { break; - for (const auto &link : current_links) { - const LayerSlice &slice_below = po->get_layer(i)->lslices_ex[link.slice_idx]; - next_links.insert(next_links.end(), slice_below.overlaps_below.begin(), slice_below.overlaps_below.end()); - std::unordered_set regions_under_to_check; - for (const LayerIsland &island : slice_below.islands) { - regions_under_to_check.insert(po->get_layer(i)->regions()[island.perimeters.region()]); - if (!island.fill_expolygons_composite()) { - regions_under_to_check.insert(po->get_layer(i)->regions()[island.fill_region_id]); - } else { - for (const auto &r : po->get_layer(i)->regions()) { - regions_under_to_check.insert(r); - } - break; - } - } - - for (const LayerRegion *region : regions_under_to_check) { - bool has_low_density = region->region().config().fill_density.value < 100; - bool has_special_infill = region_has_special_infill(region); - for (const Surface &surface : region->fill_surfaces()) { - if (surface.surface_type == stInternal && has_low_density && !has_special_infill) { - Polygons p = to_polygons(surface.expolygon); - lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), p.begin(), p.end()); - } else if (surface.surface_type == stInternal && has_low_density && has_special_infill) { - Polygons p = to_polygons(surface.expolygon); - special_infill.insert(special_infill.end(), p.begin(), p.end()); - } else { - Polygons p = to_polygons(surface.expolygon); - not_sparse_infill.insert(not_sparse_infill.end(), p.begin(), p.end()); - } - } - } } - current_links = next_links; - next_links.clear(); } - lower_layers_sparse_infill = intersection(lower_layers_sparse_infill, - layer->lslices[int(candidates.first - layer->lslices_ex.data())]); - lower_layers_sparse_infill = diff(lower_layers_sparse_infill, not_sparse_infill); - special_infill = intersection(special_infill, layer->lslices[int(candidates.first - layer->lslices_ex.data())]); - special_infill = diff(special_infill, not_sparse_infill); + // Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors + Polygons expansion_area; + for (const LayerRegion *region : layer->regions()) { + auto polys = to_polygons(region->fill_surfaces().filter_by_type(stInternal)); + expansion_area.insert(expansion_area.end(), polys.begin(), polys.end()); + } + expansion_area = closing(expansion_area, SCALED_EPSILON); + expansion_area = intersection(expansion_area, deep_infill_area); + Lines anchors = to_lines(intersection_pl(infill_lines[lidx - 1], expansion_area)); - lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), special_infill.begin(), special_infill.end()); + std::vector expanded_surfaces; + expanded_surfaces.reserve(surfaces_by_layer[lidx].size()); + for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) { + const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true); + Polygons area_to_be_bridged = intersection(candidate.new_polys, deep_infill_area); - if (shrink(lower_layers_sparse_infill, 3.0 * scale_(max_bridge_flow_height[candidates.first])).empty()) { + if (area_to_be_bridged.empty()) continue; - } - } - if (expansion_space[candidates.first].empty() && special_infill.empty()) { - // there is no expansion space to which can anchors expand on this island, add back original polygons and skip the island - for (const Surface *candidate : candidates.second) { - bridging_surfaces[candidates.first].emplace_back(candidate, to_polygons(candidate->expolygon), - surface_to_region[candidate], 0); - } - continue; - } - - Polygons expand_area; - for (const Surface *sparse_infill : expansion_space[candidates.first]) { - assert(sparse_infill->surface_type == stInternal); - Polygons a = to_polygons(sparse_infill->expolygon); - expand_area.insert(expand_area.end(), a.begin(), a.end()); - } - - // Presort the candidate polygons. This will help choose the same angle for neighbournig surfaces, that would otherwise - // compete over anchoring sparse infill lines, leaving one area unachored - std::sort(candidates.second.begin(), candidates.second.end(), [](const Surface* left, const Surface* right){ - auto a = get_extents(left->expolygon); - auto b = get_extents(right->expolygon); - - if (a.min.x() == b.min.x()) { - return a.min.y() < b.min.y(); - }; - return a.min.x() < b.min.x(); - }); - - std::unordered_map> infill_and_deep_infill_polygons_per_region; - for (const auto &surface_region : surface_to_region) { - const LayerRegion *r = surface_region.second; - if (infill_and_deep_infill_polygons_per_region.find(r) == infill_and_deep_infill_polygons_per_region.end()) { - const Flow &flow = r->bridging_flow(frSolidInfill, true); - Polygons infill_region = to_polygons(r->fill_expolygons()); - Polygons deep_infill_area = closing(infill_region, scale_(0.01), scale_(0.01) + 4.0 * flow.scaled_spacing()); - Polygons solid_supported_area = expand(not_sparse_infill, 4.0 * flow.scaled_spacing()); - infill_and_deep_infill_polygons_per_region[r] = {closing(infill_region, float(scale_(0.1))), - intersection(lower_layers_sparse_infill, - diff(deep_infill_area, solid_supported_area))}; - } - } - - // Lower layers sparse infill sections gathered - // now we can intersected them with bridging surface candidates to get actual areas that need and can accumulate - // bridging. These areas we then expand (within the surrounding sparse infill only!) - // to touch the infill polylines on previous layer. - for (const Surface *candidate : candidates.second) { - const Flow &flow = surface_to_region[candidate]->bridging_flow(frSolidInfill, true); - assert(candidate->surface_type == stInternalSolid); - - Polygons bridged_area = intersection(expand(to_polygons(candidate->expolygon), flow.scaled_spacing()), - infill_and_deep_infill_polygons_per_region[surface_to_region[candidate]].first); - // cut off parts which are not over sparse infill - material overflow - Polygons worth_bridging = intersection(bridged_area, - infill_and_deep_infill_polygons_per_region[surface_to_region[candidate]].second); - if (worth_bridging.empty()) { + Polygons boundary_area = union_(expansion_area, expand(area_to_be_bridged, flow.scaled_spacing())); + Lines boundary_lines = to_lines(boundary_area); + if (boundary_lines.empty()) continue; - } - bridged_area = intersection(bridged_area, expand(worth_bridging, 5.0 * flow.scaled_spacing())); - Polygons max_area = expand_area; - max_area.insert(max_area.end(), bridged_area.begin(), bridged_area.end()); - max_area = closing(max_area, flow.scaled_spacing()); - - Polylines anchors = intersection_pl(lower_layer_polylines, max_area); - if (!special_infill.empty()) { - auto part_over_special_infill = intersection(special_infill, bridged_area); - auto artificial_boundary = to_polylines(expand(part_over_special_infill, 0.5 * flow.scaled_width())); - anchors.insert(anchors.end(), artificial_boundary.begin(), artificial_boundary.end()); - -#ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(lidx) + "special", to_lines(part_over_special_infill), to_lines(artificial_boundary), - to_lines(anchors), to_lines(expand_area)); -#endif - } - anchors = diff_pl(anchors, bridged_area); - - Lines anchors_and_walls = to_lines(anchors); - Lines tmp = to_lines(max_area); - anchors_and_walls.insert(anchors_and_walls.end(), tmp.begin(), tmp.end()); - -#ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(lidx) + "candidate", to_lines(candidate->expolygon), to_lines(bridged_area), - to_lines(max_area), (anchors_and_walls)); -#endif - - double bridging_angle = 0; - Polygons tmp_expanded_area = expand(bridged_area, 3.0 * flow.scaled_spacing()); - for (const ModifiedSurface& s : bridging_surfaces[candidates.first]) { + double bridging_angle = 0; + Polygons tmp_expanded_area = expand(area_to_be_bridged, 3.0 * flow.scaled_spacing()); + for (const CandidateSurface &s : expanded_surfaces) { if (!intersection(s.new_polys, tmp_expanded_area).empty()) { bridging_angle = s.bridge_angle; break; } } if (bridging_angle == 0) { - AABBTreeLines::LinesDistancer lines_tree{anchors.empty() ? anchors_and_walls : to_lines(anchors)}; - - std::map counted_directions; - for (const Polygon &p : bridged_area) { - for (int point_idx = 0; point_idx < int(p.points.size()) - 1; ++point_idx) { - Vec2d start = p.points[point_idx].cast(); - Vec2d next = p.points[point_idx + 1].cast(); - Vec2d v = next - start; // vector from next to current - double dist_to_next = v.norm(); - v.normalize(); - int lines_count = int(std::ceil(dist_to_next / scaled(3.0))); - float step_size = dist_to_next / lines_count; - for (int i = 0; i < lines_count; ++i) { - Point a = (start + v * (i * step_size)).cast(); - auto [distance, index, p] = lines_tree.distance_from_lines_extra(a); - double angle = lines_tree.get_line(index).orientation(); - if (angle > PI) { - angle -= PI; - } - angle += PI * 0.5; - counted_directions[angle]++; - } - } - } - - std::pair best_dir{0, 0}; - // sliding window accumulation - for (const auto &dir : counted_directions) { - int score_acc = 0; - double dir_acc = 0; - double window_start_angle = dir.first - PI * 0.1; - double window_end_angle = dir.first + PI * 0.1; - for (auto dirs_window = counted_directions.lower_bound(window_start_angle); - dirs_window != counted_directions.upper_bound(window_end_angle); dirs_window++) { - dir_acc += dirs_window->first * dirs_window->second; - score_acc += dirs_window->second; - } - // current span of directions is 0.5 PI to 1.5 PI (due to the aproach.). Edge values should also account for the - // opposite direction. - if (window_start_angle < 0.5 * PI) { - for (auto dirs_window = counted_directions.lower_bound(1.5 * PI - (0.5 * PI - window_start_angle)); - dirs_window != counted_directions.end(); dirs_window++) { - dir_acc += dirs_window->first * dirs_window->second; - score_acc += dirs_window->second; - } - } - if (window_start_angle > 1.5 * PI) { - for (auto dirs_window = counted_directions.begin(); - dirs_window != counted_directions.upper_bound(window_start_angle - 1.5 * PI); dirs_window++) { - dir_acc += dirs_window->first * dirs_window->second; - score_acc += dirs_window->second; - } - } - - if (score_acc > best_dir.second) { - best_dir = {dir_acc / score_acc, score_acc}; - } - } - bridging_angle = best_dir.first; - if (bridging_angle == 0) { - bridging_angle = 0.001; - } - switch (surface_to_region[candidate]->region().config().fill_pattern.value) { - case ipHilbertCurve: bridging_angle += 0.25 * PI; break; - case ipOctagramSpiral: bridging_angle += (1.0 / 16.0) * PI; break; - default: break; + if (!anchors.empty()) { + bridging_angle = determine_bridging_angle(area_to_be_bridged, anchors, + candidate.region->region().config().fill_pattern.value); + } else { + // use expansion boundaries as anchors. However the current area must be removed from such filter. + // Also, use Infill pattern that is neutral for angle determination, since there are no infill lines. + bridging_angle = determine_bridging_angle(area_to_be_bridged, boundary_lines, InfillPattern::ipLine); } } - auto lines_rotate = [](Lines &lines, double cos_angle, double sin_angle) { - for (Line &l : lines) { - double ax = double(l.a.x()); - double ay = double(l.a.y()); - l.a.x() = coord_t(round(cos_angle * ax - sin_angle * ay)); - l.a.y() = coord_t(round(cos_angle * ay + sin_angle * ax)); - double bx = double(l.b.x()); - double by = double(l.b.y()); - l.b.x() = coord_t(round(cos_angle * bx - sin_angle * by)); - l.b.y() = coord_t(round(cos_angle * by + sin_angle * bx)); - } - }; + boundary_lines.insert(boundary_lines.end(), anchors.begin(), anchors.end()); + Polygons bridged_area = construct_anchored_polygon(area_to_be_bridged, boundary_lines, flow, bridging_angle); + bridged_area = intersection(bridged_area, boundary_area); + bridged_area = opening(bridged_area, flow.scaled_spacing()); + expansion_area = diff(expansion_area, bridged_area); - auto segments_overlap = [](coord_t alow, coord_t ahigh, coord_t blow, coord_t bhigh) { - return (alow >= blow && alow <= bhigh) || (ahigh >= blow && ahigh <= bhigh) || (blow >= alow && blow <= ahigh) || - (bhigh >= alow && bhigh <= ahigh); - }; - - Polygons expanded_bridged_area{}; - double aligning_angle = -bridging_angle + PI * 0.5; - { - polygons_rotate(bridged_area, aligning_angle); - lines_rotate(anchors_and_walls, cos(aligning_angle), sin(aligning_angle)); - BoundingBox bb_x = get_extents(bridged_area); - BoundingBox bb_y = get_extents(anchors_and_walls); - - const size_t n_vlines = (bb_x.max.x() - bb_x.min.x() + flow.scaled_spacing() - 1) / flow.scaled_spacing(); - std::vector vertical_lines(n_vlines); - for (size_t i = 0; i < n_vlines; i++) { - coord_t x = bb_x.min.x() + i * flow.scaled_spacing(); - coord_t y_min = bb_y.min.y() - flow.scaled_spacing(); - coord_t y_max = bb_y.max.y() + flow.scaled_spacing(); - vertical_lines[i].a = Point{x, y_min}; - vertical_lines[i].b = Point{x, y_max}; - } - - auto anchors_and_walls_tree = AABBTreeLines::LinesDistancer{std::move(anchors_and_walls)}; - auto bridged_area_tree = AABBTreeLines::LinesDistancer{to_lines(bridged_area)}; - -#ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(lidx) + "sliced", to_lines(bridged_area), anchors_and_walls, - vertical_lines, {}); -#endif - - std::vector> polygon_sections(n_vlines); - for (size_t i = 0; i < n_vlines; i++) { - auto area_intersections = bridged_area_tree.intersections_with_line(vertical_lines[i]); - for (int intersection_idx = 0; intersection_idx < int(area_intersections.size()) - 1; intersection_idx++) { - if (bridged_area_tree.outside( - (area_intersections[intersection_idx].first + area_intersections[intersection_idx + 1].first) / 2) < 0) { - polygon_sections[i].emplace_back(area_intersections[intersection_idx].first, - area_intersections[intersection_idx + 1].first); - } - } - auto anchors_intersections = anchors_and_walls_tree.intersections_with_line(vertical_lines[i]); - - for (Line §ion : polygon_sections[i]) { - auto maybe_below_anchor = std::upper_bound(anchors_intersections.rbegin(), anchors_intersections.rend(), - section.a, - [](const Point &a, const std::pair &b) { - return a.y() > b.first.y(); - }); - if (maybe_below_anchor != anchors_intersections.rend()) { - section.a = maybe_below_anchor->first; - section.a.y() -= flow.scaled_width() * (0.5 + 1.0); - } - - auto maybe_upper_anchor = std::upper_bound(anchors_intersections.begin(), anchors_intersections.end(), - section.b, - [](const Point &a, const std::pair &b) { - return a.y() < b.first.y(); - }); - if (maybe_upper_anchor != anchors_intersections.end()) { - section.b = maybe_upper_anchor->first; - section.b.y() += flow.scaled_width() * (0.5 + 1.0); - } - } - - for (int section_idx = 0; section_idx < int(polygon_sections[i].size()) - 1; section_idx++) { - Line §ion_a = polygon_sections[i][section_idx]; - Line §ion_b = polygon_sections[i][section_idx + 1]; - if (segments_overlap(section_a.a.y(), section_a.b.y(), section_b.a.y(), section_b.b.y())) { - section_b.a = section_a.a.y() < section_b.a.y() ? section_a.a : section_b.a; - section_b.b = section_a.b.y() < section_b.b.y() ? section_b.b : section_a.b; - section_a.a = section_a.b; - } - } - - polygon_sections[i].erase(std::remove_if(polygon_sections[i].begin(), polygon_sections[i].end(), - [](const Line &s) { return s.a == s.b; }), - polygon_sections[i].end()); - } - - // reconstruct polygon from polygon sections - struct TracedPoly - { - std::vector lows; - std::vector highs; - }; - - std::vector current_traced_polys; - for (const auto &polygon_slice : polygon_sections) { - std::unordered_set used_segments; - for (TracedPoly &traced_poly : current_traced_polys) { - auto maybe_first_overlap = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), - traced_poly.lows.back(), [](const Point &low, const Line &seg) { - return seg.b.y() > low.y(); - }); - - if (maybe_first_overlap != polygon_slice.end() && // segment exists - segments_overlap(traced_poly.lows.back().y(), traced_poly.highs.back().y(), maybe_first_overlap->a.y(), - maybe_first_overlap->b.y())) // segment is overlapping - { - // Overlapping segment. In that case, add it - // to the traced polygon and add segment to used segments - if ((traced_poly.lows.back() - maybe_first_overlap->a).cast().squaredNorm() < - 36.0 * double(flow.scaled_spacing()) * flow.scaled_spacing()) { - traced_poly.lows.push_back(maybe_first_overlap->a); - } else { - traced_poly.lows.push_back(traced_poly.lows.back() + Point{flow.scaled_spacing() / 2, 0}); - traced_poly.lows.push_back(maybe_first_overlap->a - Point{flow.scaled_spacing() / 2, 0}); - traced_poly.lows.push_back(maybe_first_overlap->a); - } - - if ((traced_poly.highs.back() - maybe_first_overlap->b).cast().squaredNorm() < - 36.0 * double(flow.scaled_spacing()) * flow.scaled_spacing()) { - traced_poly.highs.push_back(maybe_first_overlap->b); - } else { - traced_poly.highs.push_back(traced_poly.highs.back() + Point{flow.scaled_spacing() / 2, 0}); - traced_poly.highs.push_back(maybe_first_overlap->b - Point{flow.scaled_spacing() / 2, 0}); - traced_poly.highs.push_back(maybe_first_overlap->b); - } - used_segments.insert(&(*maybe_first_overlap)); - } else { - // Zero or multiple overlapping segments. Resolving this is nontrivial, - // so we just close this polygon and maybe open several new. This will hopefully happen much less often - traced_poly.lows.push_back(traced_poly.lows.back() + Point{flow.scaled_spacing() / 2, 0}); - traced_poly.highs.push_back(traced_poly.highs.back() + Point{flow.scaled_spacing() / 2, 0}); - Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows)); - new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); - traced_poly.lows.clear(); - traced_poly.highs.clear(); - } - } - - current_traced_polys.erase(std::remove_if(current_traced_polys.begin(), current_traced_polys.end(), - [](const TracedPoly &tp) { return tp.lows.empty(); }), - current_traced_polys.end()); - - for (const auto &segment : polygon_slice) { - if (used_segments.find(&segment) == used_segments.end()) { - TracedPoly &new_tp = current_traced_polys.emplace_back(); - new_tp.lows.push_back(segment.a - Point{flow.scaled_spacing() / 2, 0}); - new_tp.lows.push_back(segment.a); - new_tp.highs.push_back(segment.b - Point{flow.scaled_spacing() / 2, 0}); - new_tp.highs.push_back(segment.b); - } - } - } - - // add not closed polys - for (TracedPoly &traced_poly : current_traced_polys) { - Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows)); - new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); - } - -#ifdef DEBUG_BRIDGE_OVER_INFILL - Lines l{}; - for (const auto &s : polygon_sections) { - l.insert(l.end(), s.begin(), s.end()); - } - debug_draw(std::to_string(lidx) + "reconstructed", l, anchors_and_walls_tree.get_lines(), - to_lines(expanded_bridged_area), bridged_area_tree.get_lines()); -#endif - } - - polygons_rotate(expanded_bridged_area, -aligning_angle); - expanded_bridged_area = intersection(expanded_bridged_area, max_area); - expanded_bridged_area = opening(expanded_bridged_area, flow.scaled_spacing()); - expand_area = diff(expand_area, expanded_bridged_area); - - bridging_surfaces[candidates.first].emplace_back(candidate, expanded_bridged_area, surface_to_region[candidate], - bridging_angle); -#ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(lidx) + "cadidate_added", to_lines(expanded_bridged_area), to_lines(bridged_area), - to_lines(max_area), to_lines(expand_area)); -#endif + expanded_surfaces.push_back(CandidateSurface(candidate.original_surface, bridged_area, candidate.region, bridging_angle)); } + surfaces_by_layer[lidx].swap(expanded_surfaces); + expanded_surfaces.clear(); } } - }); + }); BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Directions and expanded surfaces computed" << log_memory_info(); tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = this, - &bridging_surfaces](tbb::blocked_range r) { + &surfaces_by_layer](tbb::blocked_range r) { for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { + if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end()) + continue; Layer *layer = po->get_layer(lidx); - std::unordered_map new_surfaces; - for (const LayerSlice &slice : layer->lslices_ex) { - if (const auto &modified_surfaces = bridging_surfaces.find(&slice); - modified_surfaces != bridging_surfaces.end()) { - std::unordered_set regions_to_check; - for (const LayerIsland &island : slice.islands) { - regions_to_check.insert(layer->regions()[island.perimeters.region()]); - if (!island.fill_expolygons_composite()) { - regions_to_check.insert(layer->regions()[island.fill_region_id]); - } else { - for (LayerRegion *r : layer->regions()) { - regions_to_check.insert(r); - } - break; - } - } - - Polygons cut_from_infill{}; - for (const auto &surface : modified_surfaces->second) { - cut_from_infill.insert(cut_from_infill.end(), surface.new_polys.begin(), surface.new_polys.end()); - } - - for (const LayerRegion *region : regions_to_check) { - for (const ModifiedSurface &s : modified_surfaces->second) { - for (const Surface &surface : region->m_fill_surfaces.surfaces) { - if (s.original_surface == &surface) { - Surface tmp(surface, {}); - for (const ExPolygon &expoly : diff_ex(surface.expolygon, s.new_polys)) { - if (expoly.area() > region->flow(frSolidInfill).scaled_width() * scale_(4.0)) { - new_surfaces[region].emplace_back(tmp, expoly); - } - } - tmp.surface_type = stInternalBridge; - tmp.bridge_angle = s.bridge_angle; - for (const ExPolygon &expoly : union_ex(s.new_polys)) { - new_surfaces[region].emplace_back(tmp, expoly); - } - } else if (surface.surface_type == stInternal) { - Surface tmp(surface, {}); - for (const ExPolygon &expoly : diff_ex(surface.expolygon, cut_from_infill)) { - new_surfaces[region].emplace_back(tmp, expoly); - } - } else { - new_surfaces[region].push_back(surface); - } - } - } - } - } + Polygons cut_from_infill{}; + for (const auto &surface : surfaces_by_layer.at(lidx)) { + cut_from_infill.insert(cut_from_infill.end(), surface.new_polys.begin(), surface.new_polys.end()); } for (LayerRegion *region : layer->regions()) { - if (new_surfaces.find(region) != new_surfaces.end()) { - region->m_fill_surfaces = new_surfaces[region]; + Surfaces new_surfaces; + + for (const CandidateSurface &cs : surfaces_by_layer.at(lidx)) { + for (Surface &surface : region->m_fill_surfaces.surfaces) { + if (cs.original_surface == &surface) { + Surface tmp(surface, {}); + for (const ExPolygon &expoly : diff_ex(surface.expolygon, cs.new_polys)) { + if (expoly.area() > region->flow(frSolidInfill).scaled_width() * scale_(4.0)) { + new_surfaces.emplace_back(tmp, expoly); + } + } + tmp.surface_type = stInternalBridge; + tmp.bridge_angle = cs.bridge_angle; + for (const ExPolygon &expoly : union_ex(cs.new_polys)) { + new_surfaces.emplace_back(tmp, expoly); + } + surface.clear(); + } else if (surface.surface_type == stInternal) { + Surface tmp(surface, {}); + for (const ExPolygon &expoly : diff_ex(surface.expolygon, cut_from_infill)) { + new_surfaces.emplace_back(tmp, expoly); + } + surface.clear(); + } + } } + region->m_fill_surfaces.surfaces.insert(region->m_fill_surfaces.surfaces.end(), new_surfaces.begin(), + new_surfaces.end()); + region->m_fill_surfaces.surfaces.erase(std::remove_if(region->m_fill_surfaces.surfaces.begin(), + region->m_fill_surfaces.surfaces.end(), + [](const Surface &s) { return s.empty(); }), + region->m_fill_surfaces.surfaces.end()); } } }); From 3782d24ccfbf027ccf910d4c7c029eeb2d252c63 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 9 Mar 2023 17:04:35 +0100 Subject: [PATCH 05/17] Fixed bugs with bridging area determination --- src/libslic3r/PrintObject.cpp | 113 +++++++++++++++++----------------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 42e652437..ad2f9da3c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1604,26 +1604,28 @@ void PrintObject::bridge_over_infill() continue; } auto spacing = layer->regions().front()->flow(frSolidInfill, true).scaled_spacing(); - Polygons internal_area = shrink(to_polygons(layer->lower_layer->lslices), 4 * spacing); + ExPolygons internal_area; Polygons lower_layer_solids; for (const LayerRegion *region : layer->lower_layer->regions()) { - bool has_low_density = region->region().config().fill_density.value < 100; + internal_area.insert(internal_area.end(), region->fill_expolygons().begin(), region->fill_expolygons().end()); for (const Surface &surface : region->fill_surfaces()) { - if (surface.surface_type == stInternal && has_low_density) { + if (surface.surface_type != stInternal || region->region().config().fill_density.value == 100) { Polygons p = to_polygons(surface.expolygon); lower_layer_solids.insert(lower_layer_solids.end(), p.begin(), p.end()); } } } - lower_layer_solids = expand(lower_layer_solids, 4 * spacing); - internal_area = diff(internal_area, lower_layer_solids); + lower_layer_solids = expand(lower_layer_solids, 4 * spacing); + Polygons unsupported_area = to_polygons(internal_area); + unsupported_area = shrink(unsupported_area, 4 * spacing); + unsupported_area = diff(unsupported_area, lower_layer_solids); for (const LayerRegion *region : layer->regions()) { SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); for (const Surface *s : region_internal_solids) { - Polygons away_from_perimeter = intersection(to_polygons(s->expolygon), internal_area); - if (!away_from_perimeter.empty()) { - Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(away_from_perimeter, 5 * spacing)); + Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); + if (!unsupported.empty()) { + Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0)); } } @@ -1956,11 +1958,10 @@ void PrintObject::bridge_over_infill() tbb::parallel_for(tbb::blocked_range(0, clustered_layers_for_threads.size()), [po = this, &surfaces_by_layer, &clustered_layers_for_threads, - &gather_areas_w_depth, - &infill_lines, + &gather_areas_w_depth, &infill_lines, &determine_bridging_angle, - &construct_anchored_polygon] - (tbb::blocked_range r) { + &construct_anchored_polygon]( + tbb::blocked_range r) { for (size_t cluster_idx = r.begin(); cluster_idx < r.end(); cluster_idx++) { for (size_t job_idx = 0; job_idx < clustered_layers_for_threads[cluster_idx].size(); job_idx++) { size_t lidx = clustered_layers_for_threads[cluster_idx][job_idx]; @@ -2000,6 +2001,7 @@ void PrintObject::bridge_over_infill() break; } } + } // Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors Polygons expansion_area; @@ -2046,11 +2048,12 @@ void PrintObject::bridge_over_infill() boundary_lines.insert(boundary_lines.end(), anchors.begin(), anchors.end()); Polygons bridged_area = construct_anchored_polygon(area_to_be_bridged, boundary_lines, flow, bridging_angle); - bridged_area = intersection(bridged_area, boundary_area); - bridged_area = opening(bridged_area, flow.scaled_spacing()); - expansion_area = diff(expansion_area, bridged_area); + bridged_area = intersection(bridged_area, boundary_area); + bridged_area = opening(bridged_area, flow.scaled_spacing()); + expansion_area = diff(expansion_area, bridged_area); - expanded_surfaces.push_back(CandidateSurface(candidate.original_surface, bridged_area, candidate.region, bridging_angle)); + expanded_surfaces.push_back( + CandidateSurface(candidate.original_surface, bridged_area, candidate.region, bridging_angle)); } surfaces_by_layer[lidx].swap(expanded_surfaces); expanded_surfaces.clear(); @@ -2058,58 +2061,56 @@ void PrintObject::bridge_over_infill() } }); - BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Directions and expanded surfaces computed" << log_memory_info(); + BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Directions and expanded surfaces computed" << log_memory_info(); - tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = this, - &surfaces_by_layer](tbb::blocked_range r) { - for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { - if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end()) - continue; - Layer *layer = po->get_layer(lidx); + tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = this, &surfaces_by_layer](tbb::blocked_range r) { + for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { + if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end()) + continue; + Layer *layer = po->get_layer(lidx); - Polygons cut_from_infill{}; - for (const auto &surface : surfaces_by_layer.at(lidx)) { - cut_from_infill.insert(cut_from_infill.end(), surface.new_polys.begin(), surface.new_polys.end()); - } + Polygons cut_from_infill{}; + for (const auto &surface : surfaces_by_layer.at(lidx)) { + cut_from_infill.insert(cut_from_infill.end(), surface.new_polys.begin(), surface.new_polys.end()); + } - for (LayerRegion *region : layer->regions()) { - Surfaces new_surfaces; + for (LayerRegion *region : layer->regions()) { + Surfaces new_surfaces; - for (const CandidateSurface &cs : surfaces_by_layer.at(lidx)) { - for (Surface &surface : region->m_fill_surfaces.surfaces) { - if (cs.original_surface == &surface) { - Surface tmp(surface, {}); - for (const ExPolygon &expoly : diff_ex(surface.expolygon, cs.new_polys)) { - if (expoly.area() > region->flow(frSolidInfill).scaled_width() * scale_(4.0)) { - new_surfaces.emplace_back(tmp, expoly); - } - } - tmp.surface_type = stInternalBridge; - tmp.bridge_angle = cs.bridge_angle; - for (const ExPolygon &expoly : union_ex(cs.new_polys)) { + for (const CandidateSurface &cs : surfaces_by_layer.at(lidx)) { + for (Surface &surface : region->m_fill_surfaces.surfaces) { + if (cs.original_surface == &surface) { + Surface tmp(surface, {}); + for (const ExPolygon &expoly : diff_ex(surface.expolygon, cs.new_polys)) { + if (expoly.area() > region->flow(frSolidInfill).scaled_width() * scale_(4.0)) { new_surfaces.emplace_back(tmp, expoly); } - surface.clear(); - } else if (surface.surface_type == stInternal) { - Surface tmp(surface, {}); - for (const ExPolygon &expoly : diff_ex(surface.expolygon, cut_from_infill)) { - new_surfaces.emplace_back(tmp, expoly); - } - surface.clear(); } + tmp.surface_type = stInternalBridge; + tmp.bridge_angle = cs.bridge_angle; + for (const ExPolygon &expoly : union_ex(cs.new_polys)) { + new_surfaces.emplace_back(tmp, expoly); + } + surface.clear(); + } else if (surface.surface_type == stInternal) { + Surface tmp(surface, {}); + for (const ExPolygon &expoly : diff_ex(surface.expolygon, cut_from_infill)) { + new_surfaces.emplace_back(tmp, expoly); + } + surface.clear(); } } - region->m_fill_surfaces.surfaces.insert(region->m_fill_surfaces.surfaces.end(), new_surfaces.begin(), - new_surfaces.end()); - region->m_fill_surfaces.surfaces.erase(std::remove_if(region->m_fill_surfaces.surfaces.begin(), - region->m_fill_surfaces.surfaces.end(), - [](const Surface &s) { return s.empty(); }), - region->m_fill_surfaces.surfaces.end()); } + region->m_fill_surfaces.surfaces.insert(region->m_fill_surfaces.surfaces.end(), new_surfaces.begin(), new_surfaces.end()); + region->m_fill_surfaces.surfaces.erase(std::remove_if(region->m_fill_surfaces.surfaces.begin(), + region->m_fill_surfaces.surfaces.end(), + [](const Surface &s) { return s.empty(); }), + region->m_fill_surfaces.surfaces.end()); } - }); + } + }); - BOOST_LOG_TRIVIAL(info) << "Bridge over infill - End" << log_memory_info(); + BOOST_LOG_TRIVIAL(info) << "Bridge over infill - End" << log_memory_info(); } // void PrintObject::bridge_over_infill() From ad693532d3167f8aca5103e9fd824a79ffdaf393 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 10 Mar 2023 13:13:40 +0100 Subject: [PATCH 06/17] Fixed clustering for threads issue - inverse comparison, lower job idx incorrectly initialized --- src/libslic3r/PrintObject.cpp | 55 +++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index ad2f9da3c..4ee24235d 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -1633,6 +1634,13 @@ void PrintObject::bridge_over_infill() } }); +#ifdef DEBUG_BRIDGE_OVER_INFILL + for (const auto &c : candidate_surfaces) { + debug_draw(std::to_string(c.region->layer()->id()) + "_candidate_surface_" + std::to_string(area(c.original_surface->expolygon)), + to_lines(c.region->layer()->lslices), to_lines(c.original_surface->expolygon), to_lines(c.new_polys), {}); + } +#endif + std::map> surfaces_by_layer; std::vector> surfaces_w_bottom_z; for (const CandidateSurface& c : candidate_surfaces) { @@ -1661,10 +1669,17 @@ void PrintObject::bridge_over_infill() } }); +#ifdef DEBUG_BRIDGE_OVER_INFILL + for (const auto &il : infill_lines) { + debug_draw(std::to_string(il.first) + "_infill_lines", to_lines(get_layer(il.first)->lslices), to_lines(il.second), {}, {}); + } +#endif + // cluster layers by depth needed for thick bridges. Each cluster is to be processed by single thread sequentially, so that bridges cannot appear one on another std::vector> clustered_layers_for_threads; + // note: surfaces_by_layer is ordered map for (auto pair : surfaces_by_layer) { - if (clustered_layers_for_threads.empty() || this->get_layer(clustered_layers_for_threads.back().back())->print_z > + if (clustered_layers_for_threads.empty() || this->get_layer(clustered_layers_for_threads.back().back())->print_z < this->get_layer(pair.first)->print_z - this->get_layer(pair.first)->regions()[0]->flow(frSolidInfill, true).height() - EPSILON) { @@ -1674,6 +1689,17 @@ void PrintObject::bridge_over_infill() } } +#ifdef DEBUG_BRIDGE_OVER_INFILL + std::cout << "BRIDGE OVER INFILL CLUSTERED LAYERS FOR SINGLE THREAD" << std::endl; + for (auto cluster : clustered_layers_for_threads) { + std::cout << "CLUSTER: "; + for (auto l : cluster) { + std::cout << l << " "; + } + std::cout << std::endl; + } +#endif + // LAMBDA to gather areas with sparse infill deep enough that we can fit thick bridges there. auto gather_areas_w_depth = [](const PrintObject *po, int lidx, float target_flow_height) { @@ -1820,10 +1846,6 @@ void PrintObject::bridge_over_infill() auto anchors_and_walls_tree = AABBTreeLines::LinesDistancer{std::move(anchors)}; auto bridged_area_tree = AABBTreeLines::LinesDistancer{to_lines(bridged_area)}; -#ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(lidx) + "sliced", to_lines(bridged_area), anchors_and_walls, vertical_lines, {}); -#endif - std::vector> polygon_sections(n_vlines); for (size_t i = 0; i < n_vlines; i++) { auto area_intersections = bridged_area_tree.intersections_with_line(vertical_lines[i]); @@ -1941,15 +1963,6 @@ void PrintObject::bridge_over_infill() Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows)); new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); } - -#ifdef DEBUG_BRIDGE_OVER_INFILL - Lines l{}; - for (const auto &s : polygon_sections) { - l.insert(l.end(), s.begin(), s.end()); - } - debug_draw(std::to_string(lidx) + "reconstructed", l, anchors_and_walls_tree.get_lines(), to_lines(expanded_bridged_area), - bridged_area_tree.get_lines()); -#endif } polygons_rotate(expanded_bridged_area, -aligning_angle); @@ -1990,7 +2003,7 @@ void PrintObject::bridge_over_infill() // reason we did the clustering of layers per thread. double bottom_z = po->get_layer(lidx)->print_z - thick_bridges_depth - EPSILON; if (job_idx > 0) { - for (int lower_job_idx = job_idx; lower_job_idx >= 0; lower_job_idx--) { + for (int lower_job_idx = job_idx - 1; lower_job_idx >= 0; lower_job_idx--) { size_t lower_layer_idx = clustered_layers_for_threads[cluster_idx][lower_job_idx]; const Layer *lower_layer = po->get_layer(lower_layer_idx); if (lower_layer->print_z >= bottom_z) { @@ -2024,9 +2037,6 @@ void PrintObject::bridge_over_infill() Polygons boundary_area = union_(expansion_area, expand(area_to_be_bridged, flow.scaled_spacing())); Lines boundary_lines = to_lines(boundary_area); - if (boundary_lines.empty()) - continue; - double bridging_angle = 0; Polygons tmp_expanded_area = expand(area_to_be_bridged, 3.0 * flow.scaled_spacing()); for (const CandidateSurface &s : expanded_surfaces) { @@ -2040,7 +2050,7 @@ void PrintObject::bridge_over_infill() bridging_angle = determine_bridging_angle(area_to_be_bridged, anchors, candidate.region->region().config().fill_pattern.value); } else { - // use expansion boundaries as anchors. However the current area must be removed from such filter. + // use expansion boundaries as anchors. // Also, use Infill pattern that is neutral for angle determination, since there are no infill lines. bridging_angle = determine_bridging_angle(area_to_be_bridged, boundary_lines, InfillPattern::ipLine); } @@ -2052,6 +2062,13 @@ void PrintObject::bridge_over_infill() bridged_area = opening(bridged_area, flow.scaled_spacing()); expansion_area = diff(expansion_area, bridged_area); +#ifdef DEBUG_BRIDGE_OVER_INFILL + debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + + "_expanded_bridging", + to_lines(layer->lslices), to_lines(candidate.original_surface->expolygon), to_lines(candidate.new_polys), + to_lines(bridged_area)); +#endif + expanded_surfaces.push_back( CandidateSurface(candidate.original_surface, bridged_area, candidate.region, bridging_angle)); } From d223eef38d2d9b1f8608b5522526ce4b1fe0a4b6 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 10 Mar 2023 15:35:11 +0100 Subject: [PATCH 07/17] Do not generate other than sparse infill lines Split jobs if candidates bounding boxes do not overlap - otherwise it can become completely linearized and very slow Improve formatting --- src/libslic3r/Fill/Fill.cpp | 4 + src/libslic3r/PrintObject.cpp | 198 ++++++++++++++++++++-------------- 2 files changed, 121 insertions(+), 81 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index a6b420607..8db49e559 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -649,6 +649,10 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc Polylines sparse_infill_polylines{}; for (SurfaceFill &surface_fill : surface_fills) { + if (surface_fill.surface.surface_type != stInternal) { + continue; + } + switch (surface_fill.params.pattern) { case ipLightning: { auto polylines = to_polylines(shrink_ex(surface_fill.expolygons, 5.0 * surface_fill.params.flow.scaled_spacing())); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 4ee24235d..8a1bacb0b 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1595,110 +1595,146 @@ void PrintObject::bridge_over_infill() double bridge_angle; }; - tbb::concurrent_vector candidate_surfaces; + std::map> surfaces_by_layer; - tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = static_cast(this), - &candidate_surfaces](tbb::blocked_range r) { - for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { - const Layer *layer = po->get_layer(lidx); - if (layer->lower_layer == nullptr) { - continue; - } - auto spacing = layer->regions().front()->flow(frSolidInfill, true).scaled_spacing(); - ExPolygons internal_area; - Polygons lower_layer_solids; - for (const LayerRegion *region : layer->lower_layer->regions()) { - internal_area.insert(internal_area.end(), region->fill_expolygons().begin(), region->fill_expolygons().end()); - for (const Surface &surface : region->fill_surfaces()) { - if (surface.surface_type != stInternal || region->region().config().fill_density.value == 100) { - Polygons p = to_polygons(surface.expolygon); - lower_layer_solids.insert(lower_layer_solids.end(), p.begin(), p.end()); + // SECTION to gather and filter surfaces for expanding, and then cluster them by layer + { + tbb::concurrent_vector candidate_surfaces; + tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = static_cast(this), + &candidate_surfaces](tbb::blocked_range r) { + for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { + const Layer *layer = po->get_layer(lidx); + if (layer->lower_layer == nullptr) { + continue; + } + auto spacing = layer->regions().front()->flow(frSolidInfill, true).scaled_spacing(); + ExPolygons internal_area; + Polygons lower_layer_solids; + for (const LayerRegion *region : layer->lower_layer->regions()) { + internal_area.insert(internal_area.end(), region->fill_expolygons().begin(), region->fill_expolygons().end()); + for (const Surface &surface : region->fill_surfaces()) { + if (surface.surface_type != stInternal || region->region().config().fill_density.value == 100) { + Polygons p = to_polygons(surface.expolygon); + lower_layer_solids.insert(lower_layer_solids.end(), p.begin(), p.end()); + } + } + } + lower_layer_solids = expand(lower_layer_solids, 4 * spacing); + Polygons unsupported_area = to_polygons(internal_area); + unsupported_area = shrink(unsupported_area, 4 * spacing); + unsupported_area = diff(unsupported_area, lower_layer_solids); + + for (const LayerRegion *region : layer->regions()) { + SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); + for (const Surface *s : region_internal_solids) { + Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); + if (!unsupported.empty()) { + Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); + candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0)); + } } } } - lower_layer_solids = expand(lower_layer_solids, 4 * spacing); - Polygons unsupported_area = to_polygons(internal_area); - unsupported_area = shrink(unsupported_area, 4 * spacing); - unsupported_area = diff(unsupported_area, lower_layer_solids); - - for (const LayerRegion *region : layer->regions()) { - SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); - for (const Surface *s : region_internal_solids) { - Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); - if (!unsupported.empty()) { - Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); - candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0)); - } - } - } - } - }); + }); #ifdef DEBUG_BRIDGE_OVER_INFILL - for (const auto &c : candidate_surfaces) { - debug_draw(std::to_string(c.region->layer()->id()) + "_candidate_surface_" + std::to_string(area(c.original_surface->expolygon)), - to_lines(c.region->layer()->lslices), to_lines(c.original_surface->expolygon), to_lines(c.new_polys), {}); - } + for (const auto &c : candidate_surfaces) { + debug_draw(std::to_string(c.region->layer()->id()) + "_candidate_surface_" + std::to_string(area(c.original_surface->expolygon)), + to_lines(c.region->layer()->lslices), to_lines(c.original_surface->expolygon), to_lines(c.new_polys), {}); + } #endif - std::map> surfaces_by_layer; - std::vector> surfaces_w_bottom_z; - for (const CandidateSurface& c : candidate_surfaces) { - surfaces_by_layer[c.region->layer()->id()].push_back(c); - surfaces_w_bottom_z.emplace_back(c.original_surface, c.region->m_layer->bottom_z()); + for (const CandidateSurface &c : candidate_surfaces) { + surfaces_by_layer[c.region->layer()->id()].push_back(c); + } } - this->adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_bottom_z); - std::map infill_lines; - std::vector layers_to_generate_infill; - for (const auto& pair : surfaces_by_layer) { - assert(pair.first > 0); - infill_lines[pair.first-1] = {}; - layers_to_generate_infill.push_back(pair.first-1); - } - - tbb::parallel_for(tbb::blocked_range(0, layers_to_generate_infill.size()), [po = static_cast(this), - &layers_to_generate_infill, - &infill_lines](tbb::blocked_range r) { - for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { - size_t lidx = layers_to_generate_infill[job_idx]; - infill_lines.at( - lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->adaptive_fill_octrees.first.get(), - po->adaptive_fill_octrees.second.get()); + // SECTION to generate infill polylines + { + std::vector> surfaces_w_bottom_z; + for (const auto &pair : surfaces_by_layer) { + for (const CandidateSurface &c : pair.second) { + surfaces_w_bottom_z.emplace_back(c.original_surface, c.region->m_layer->bottom_z()); + } } - }); + this->adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_bottom_z); + std::vector layers_to_generate_infill; + for (const auto &pair : surfaces_by_layer) { + assert(pair.first > 0); + infill_lines[pair.first - 1] = {}; + layers_to_generate_infill.push_back(pair.first - 1); + } + + tbb::parallel_for(tbb::blocked_range(0, layers_to_generate_infill.size()), [po = static_cast(this), + &layers_to_generate_infill, + &infill_lines](tbb::blocked_range r) { + for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { + size_t lidx = layers_to_generate_infill[job_idx]; + infill_lines.at( + lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->adaptive_fill_octrees.first.get(), + po->adaptive_fill_octrees.second.get()); + } + }); #ifdef DEBUG_BRIDGE_OVER_INFILL - for (const auto &il : infill_lines) { - debug_draw(std::to_string(il.first) + "_infill_lines", to_lines(get_layer(il.first)->lslices), to_lines(il.second), {}, {}); - } + for (const auto &il : infill_lines) { + debug_draw(std::to_string(il.first) + "_infill_lines", to_lines(get_layer(il.first)->lslices), to_lines(il.second), {}, {}); + } #endif + } // cluster layers by depth needed for thick bridges. Each cluster is to be processed by single thread sequentially, so that bridges cannot appear one on another std::vector> clustered_layers_for_threads; - // note: surfaces_by_layer is ordered map - for (auto pair : surfaces_by_layer) { - if (clustered_layers_for_threads.empty() || this->get_layer(clustered_layers_for_threads.back().back())->print_z < - this->get_layer(pair.first)->print_z - - this->get_layer(pair.first)->regions()[0]->flow(frSolidInfill, true).height() - - EPSILON) { - clustered_layers_for_threads.push_back({pair.first}); - } else { - clustered_layers_for_threads.back().push_back(pair.first); + { + std::vector layers_with_candidates; + std::map layer_area_covered_by_candidates; + for (const auto& pair : surfaces_by_layer) { + layers_with_candidates.push_back(pair.first); + layer_area_covered_by_candidates[pair.first] = {}; + } + + tbb::parallel_for(tbb::blocked_range(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer, + &layer_area_covered_by_candidates]( + tbb::blocked_range r) { + for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { + size_t lidx = layers_with_candidates[job_idx]; + for (const auto &candidate : surfaces_by_layer.at(lidx)) { + Polygon candiate_inflated_aabb = get_extents(candidate.new_polys) + .inflated(candidate.region->flow(frSolidInfill, true).scaled_spacing() * 5) + .polygon(); + layer_area_covered_by_candidates.at(lidx) = union_(layer_area_covered_by_candidates.at(lidx), + Polygons{candiate_inflated_aabb}); + } + } + }); + + // note: surfaces_by_layer is ordered map + for (auto pair : surfaces_by_layer) { + if (clustered_layers_for_threads.empty() || + this->get_layer(clustered_layers_for_threads.back().back())->print_z < + this->get_layer(pair.first)->print_z - this->get_layer(pair.first)->regions()[0]->flow(frSolidInfill, true).height() - + EPSILON || + intersection(layer_area_covered_by_candidates[clustered_layers_for_threads.back().back()], + layer_area_covered_by_candidates[pair.first]) + .empty()) { + clustered_layers_for_threads.push_back({pair.first}); + } else { + clustered_layers_for_threads.back().push_back(pair.first); + } } - } #ifdef DEBUG_BRIDGE_OVER_INFILL - std::cout << "BRIDGE OVER INFILL CLUSTERED LAYERS FOR SINGLE THREAD" << std::endl; - for (auto cluster : clustered_layers_for_threads) { - std::cout << "CLUSTER: "; - for (auto l : cluster) { - std::cout << l << " "; + std::cout << "BRIDGE OVER INFILL CLUSTERED LAYERS FOR SINGLE THREAD" << std::endl; + for (auto cluster : clustered_layers_for_threads) { + std::cout << "CLUSTER: "; + for (auto l : cluster) { + std::cout << l << " "; + } + std::cout << std::endl; } - std::cout << std::endl; - } #endif + } // LAMBDA to gather areas with sparse infill deep enough that we can fit thick bridges there. auto gather_areas_w_depth = From 76209d89ff16f5adb51d26f0aaa63fab84591388 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 10 Mar 2023 16:59:21 +0100 Subject: [PATCH 08/17] Fixed lighting infill crash. TODO filtering of small ensuring regions --- src/libslic3r/Fill/Fill.cpp | 1 + src/libslic3r/PrintObject.cpp | 32 +++++++++++++++++--------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 8db49e559..e42c4babb 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -657,6 +657,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc case ipLightning: { auto polylines = to_polylines(shrink_ex(surface_fill.expolygons, 5.0 * surface_fill.params.flow.scaled_spacing())); sparse_infill_polylines.insert(sparse_infill_polylines.end(), polylines.begin(), polylines.end()); + continue; }; break; case ipCount: continue; break; case ipSupportBase: continue; break; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 8a1bacb0b..38e3d3400 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1560,7 +1560,7 @@ void PrintObject::discover_vertical_shells() } // for each region } // void PrintObject::discover_vertical_shells() -// #define DEBUG_BRIDGE_OVER_INFILL +#define DEBUG_BRIDGE_OVER_INFILL #ifdef DEBUG_BRIDGE_OVER_INFILL template void debug_draw(std::string name, const T& a, const T& b, const T& c, const T& d) { @@ -1608,10 +1608,11 @@ void PrintObject::bridge_over_infill() continue; } auto spacing = layer->regions().front()->flow(frSolidInfill, true).scaled_spacing(); - ExPolygons internal_area; + Polygons unsupported_area; Polygons lower_layer_solids; for (const LayerRegion *region : layer->lower_layer->regions()) { - internal_area.insert(internal_area.end(), region->fill_expolygons().begin(), region->fill_expolygons().end()); + Polygons fill_polys = to_polygons(region->fill_expolygons()); + unsupported_area = union_(unsupported_area, fill_polys); for (const Surface &surface : region->fill_surfaces()) { if (surface.surface_type != stInternal || region->region().config().fill_density.value == 100) { Polygons p = to_polygons(surface.expolygon); @@ -1619,31 +1620,32 @@ void PrintObject::bridge_over_infill() } } } - lower_layer_solids = expand(lower_layer_solids, 4 * spacing); - Polygons unsupported_area = to_polygons(internal_area); - unsupported_area = shrink(unsupported_area, 4 * spacing); - unsupported_area = diff(unsupported_area, lower_layer_solids); + + //TODO if region touches the extremes, then check for its area and filter. otherwise, keep even the smallest one + + lower_layer_solids = expand(lower_layer_solids, 4 * spacing); + unsupported_area = shrink(unsupported_area, 4 * spacing); + unsupported_area = diff(unsupported_area, lower_layer_solids); for (const LayerRegion *region : layer->regions()) { SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); for (const Surface *s : region_internal_solids) { Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); - if (!unsupported.empty()) { + if (area(unsupported) > spacing * spacing) { Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0)); + +#ifdef DEBUG_BRIDGE_OVER_INFILL + debug_draw(std::to_string(region->layer()->id()) + "_candidate_surface_" + std::to_string(area(s->expolygon)), + to_lines(region->layer()->lslices), to_lines(s->expolygon), to_lines(worth_bridging), + to_lines(unsupported_area)); +#endif } } } } }); -#ifdef DEBUG_BRIDGE_OVER_INFILL - for (const auto &c : candidate_surfaces) { - debug_draw(std::to_string(c.region->layer()->id()) + "_candidate_surface_" + std::to_string(area(c.original_surface->expolygon)), - to_lines(c.region->layer()->lslices), to_lines(c.original_surface->expolygon), to_lines(c.new_polys), {}); - } -#endif - for (const CandidateSurface &c : candidate_surfaces) { surfaces_by_layer[c.region->layer()->id()].push_back(c); } From 487d9209edf0db1620d409a2a2dd614c215a8454 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 13 Mar 2023 13:02:08 +0100 Subject: [PATCH 09/17] Fix and improve region filtering --- src/libslic3r/PrintObject.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 38e3d3400..0ef6043e1 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1607,12 +1607,12 @@ void PrintObject::bridge_over_infill() if (layer->lower_layer == nullptr) { continue; } - auto spacing = layer->regions().front()->flow(frSolidInfill, true).scaled_spacing(); + auto spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing(); Polygons unsupported_area; Polygons lower_layer_solids; for (const LayerRegion *region : layer->lower_layer->regions()) { Polygons fill_polys = to_polygons(region->fill_expolygons()); - unsupported_area = union_(unsupported_area, fill_polys); + unsupported_area = union_(unsupported_area, expand(fill_polys, spacing)); for (const Surface &surface : region->fill_surfaces()) { if (surface.surface_type != stInternal || region->region().config().fill_density.value == 100) { Polygons p = to_polygons(surface.expolygon); @@ -1621,17 +1621,16 @@ void PrintObject::bridge_over_infill() } } - //TODO if region touches the extremes, then check for its area and filter. otherwise, keep even the smallest one - lower_layer_solids = expand(lower_layer_solids, 4 * spacing); - unsupported_area = shrink(unsupported_area, 4 * spacing); + unsupported_area = shrink(unsupported_area, 5 * spacing); unsupported_area = diff(unsupported_area, lower_layer_solids); for (const LayerRegion *region : layer->regions()) { SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); for (const Surface *s : region_internal_solids) { - Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); - if (area(unsupported) > spacing * spacing) { + Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); + bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON; + if (!unsupported.empty() && (!partially_supported || area(unsupported) > 5 * 5 * spacing * spacing)) { Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0)); From 12f1cd0bc06e21f3e5230a19f08d8f0bb57e01c1 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 13 Mar 2023 16:08:32 +0100 Subject: [PATCH 10/17] added semi support for lightning infill --- src/libslic3r/Fill/Fill.cpp | 14 ++++--- src/libslic3r/Layer.hpp | 3 +- src/libslic3r/Print.hpp | 4 +- src/libslic3r/PrintObject.cpp | 72 ++++++++++++++++++++++------------- 4 files changed, 58 insertions(+), 35 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index e42c4babb..a6e5e1fb4 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -640,7 +640,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: #endif } -Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree) const +Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator) const { std::vector surface_fills = group_fills(*this); const Slic3r::BoundingBox bbox = this->object()->bounding_box(); @@ -654,14 +654,10 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc } switch (surface_fill.params.pattern) { - case ipLightning: { - auto polylines = to_polylines(shrink_ex(surface_fill.expolygons, 5.0 * surface_fill.params.flow.scaled_spacing())); - sparse_infill_polylines.insert(sparse_infill_polylines.end(), polylines.begin(), polylines.end()); - continue; - }; break; case ipCount: continue; break; case ipSupportBase: continue; break; case ipEnsuring: continue; break; + case ipLightning: case ipAdaptiveCubic: case ipSupportCubic: case ipRectilinear: @@ -692,6 +688,12 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc f->print_config = &this->object()->print()->config(); f->print_object_config = &this->object()->config(); + if (surface_fill.params.pattern == ipLightning) { + auto *lf = dynamic_cast(f.get()); + lf->generator = lightning_generator; + lf->num_raft_layers = this->object()->slicing_parameters().raft_layers(); + } + // calculate flow spacing for infill pattern generation double link_max_length = 0.; if (!surface_fill.params.bridge) { diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 0cba55d3c..b3d071c9d 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -372,7 +372,8 @@ public: FillAdaptive::Octree *support_fill_octree, FillLightning::Generator *lightning_generator); Polylines generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree *adaptive_fill_octree, - FillAdaptive::Octree *support_fill_octree) const; + FillAdaptive::Octree *support_fill_octree, + FillLightning::Generator* lightning_generator) const; void make_ironing(); void export_region_slices_to_svg(const char *path) const; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index df2bde469..9fbbe378a 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -2,6 +2,7 @@ #define slic3r_Print_hpp_ #include "Fill/FillAdaptive.hpp" +#include "Fill/FillLightning.hpp" #include "PrintBase.hpp" #include "BoundingBox.hpp" @@ -413,7 +414,8 @@ private: // so that next call to make_perimeters() performs a union() before computing loops bool m_typed_slices = false; - std::pair adaptive_fill_octrees; + std::pair m_adaptive_fill_octrees; + FillLightning::GeneratorPtr m_lightning_generator; }; struct WipeTowerData diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 0ef6043e1..2bd19ba5a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -404,17 +404,16 @@ void PrintObject::infill() if (this->set_started(posInfill)) { m_print->set_status(45, L("making infill")); - const auto& adaptive_fill_octree = this->adaptive_fill_octrees.first; - const auto& support_fill_octree = this->adaptive_fill_octrees.second; - auto lightning_generator = this->prepare_lightning_infill_data(); + const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first; + const auto& support_fill_octree = this->m_adaptive_fill_octrees.second; BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), - [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree, &lightning_generator](const tbb::blocked_range& range) { + [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); - m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), lightning_generator.get()); + m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), this->m_lightning_generator.get()); } } ); @@ -1560,7 +1559,7 @@ void PrintObject::discover_vertical_shells() } // for each region } // void PrintObject::discover_vertical_shells() -#define DEBUG_BRIDGE_OVER_INFILL +// #define DEBUG_BRIDGE_OVER_INFILL #ifdef DEBUG_BRIDGE_OVER_INFILL template void debug_draw(std::string name, const T& a, const T& b, const T& c, const T& d) { @@ -1586,13 +1585,22 @@ void PrintObject::bridge_over_infill() struct CandidateSurface { - CandidateSurface(const Surface *original_surface, Polygons new_polys, const LayerRegion *region, double bridge_angle) - : original_surface(original_surface), new_polys(new_polys), region(region), bridge_angle(bridge_angle) + CandidateSurface(const Surface *original_surface, + Polygons new_polys, + const LayerRegion *region, + double bridge_angle, + bool supported_by_lightning) + : original_surface(original_surface) + , new_polys(new_polys) + , region(region) + , bridge_angle(bridge_angle) + , supported_by_lightning(supported_by_lightning) {} const Surface *original_surface; Polygons new_polys; const LayerRegion *region; double bridge_angle; + bool supported_by_lightning; }; std::map> surfaces_by_layer; @@ -1610,7 +1618,11 @@ void PrintObject::bridge_over_infill() auto spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing(); Polygons unsupported_area; Polygons lower_layer_solids; + bool contains_only_lightning = true; for (const LayerRegion *region : layer->lower_layer->regions()) { + if (region->region().config().fill_pattern.value != ipLightning) { + contains_only_lightning = false; + } Polygons fill_polys = to_polygons(region->fill_expolygons()); unsupported_area = union_(unsupported_area, expand(fill_polys, spacing)); for (const Surface &surface : region->fill_surfaces()) { @@ -1632,7 +1644,7 @@ void PrintObject::bridge_over_infill() bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON; if (!unsupported.empty() && (!partially_supported || area(unsupported) > 5 * 5 * spacing * spacing)) { Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); - candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0)); + candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0, contains_only_lightning)); #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(region->layer()->id()) + "_candidate_surface_" + std::to_string(area(s->expolygon)), @@ -1660,7 +1672,9 @@ void PrintObject::bridge_over_infill() } } - this->adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_bottom_z); + this->m_adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_bottom_z); + this->m_lightning_generator = this->prepare_lightning_infill_data(); + std::vector layers_to_generate_infill; for (const auto &pair : surfaces_by_layer) { assert(pair.first > 0); @@ -1674,8 +1688,9 @@ void PrintObject::bridge_over_infill() for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { size_t lidx = layers_to_generate_infill[job_idx]; infill_lines.at( - lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->adaptive_fill_octrees.first.get(), - po->adaptive_fill_octrees.second.get()); + lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->m_adaptive_fill_octrees.first.get(), + po->m_adaptive_fill_octrees.second.get(), + po->m_lightning_generator.get()); } }); #ifdef DEBUG_BRIDGE_OVER_INFILL @@ -2061,21 +2076,21 @@ void PrintObject::bridge_over_infill() } expansion_area = closing(expansion_area, SCALED_EPSILON); expansion_area = intersection(expansion_area, deep_infill_area); - Lines anchors = to_lines(intersection_pl(infill_lines[lidx - 1], expansion_area)); + Polylines anchors = intersection_pl(infill_lines[lidx - 1], expansion_area); std::vector expanded_surfaces; expanded_surfaces.reserve(surfaces_by_layer[lidx].size()); for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) { const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true); - Polygons area_to_be_bridged = intersection(candidate.new_polys, deep_infill_area); + Polygons area_to_be_bridge = intersection(candidate.new_polys, deep_infill_area); - if (area_to_be_bridged.empty()) + if (area_to_be_bridge.empty()) continue; - Polygons boundary_area = union_(expansion_area, expand(area_to_be_bridged, flow.scaled_spacing())); - Lines boundary_lines = to_lines(boundary_area); + Polygons boundary_area = union_(expansion_area, expand(area_to_be_bridge, flow.scaled_spacing())); + Polylines boundary_plines = to_polylines(boundary_area); double bridging_angle = 0; - Polygons tmp_expanded_area = expand(area_to_be_bridged, 3.0 * flow.scaled_spacing()); + Polygons tmp_expanded_area = expand(area_to_be_bridge, 3.0 * flow.scaled_spacing()); for (const CandidateSurface &s : expanded_surfaces) { if (!intersection(s.new_polys, tmp_expanded_area).empty()) { bridging_angle = s.bridge_angle; @@ -2084,30 +2099,33 @@ void PrintObject::bridge_over_infill() } if (bridging_angle == 0) { if (!anchors.empty()) { - bridging_angle = determine_bridging_angle(area_to_be_bridged, anchors, + bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors), candidate.region->region().config().fill_pattern.value); } else { // use expansion boundaries as anchors. // Also, use Infill pattern that is neutral for angle determination, since there are no infill lines. - bridging_angle = determine_bridging_angle(area_to_be_bridged, boundary_lines, InfillPattern::ipLine); + bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(boundary_plines), InfillPattern::ipLine); } } - boundary_lines.insert(boundary_lines.end(), anchors.begin(), anchors.end()); - Polygons bridged_area = construct_anchored_polygon(area_to_be_bridged, boundary_lines, flow, bridging_angle); - bridged_area = intersection(bridged_area, boundary_area); - bridged_area = opening(bridged_area, flow.scaled_spacing()); - expansion_area = diff(expansion_area, bridged_area); + boundary_plines.insert(boundary_plines.end(), anchors.begin(), anchors.end()); + if (candidate.supported_by_lightning) { + boundary_plines = intersection_pl(boundary_plines, expand(area_to_be_bridge, scale_(10))); + } + Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle); + bridging_area = intersection(bridging_area, boundary_area); + bridging_area = opening(bridging_area, flow.scaled_spacing()); + expansion_area = diff(expansion_area, bridging_area); #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_expanded_bridging", to_lines(layer->lslices), to_lines(candidate.original_surface->expolygon), to_lines(candidate.new_polys), - to_lines(bridged_area)); + to_lines(bridging_area)); #endif expanded_surfaces.push_back( - CandidateSurface(candidate.original_surface, bridged_area, candidate.region, bridging_angle)); + CandidateSurface(candidate.original_surface, bridging_area, candidate.region, bridging_angle, candidate.supported_by_lightning)); } surfaces_by_layer[lidx].swap(expanded_surfaces); expanded_surfaces.clear(); From 822ea84e0d483e4a4d8c5bdbb82c7a89e35618e2 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 13 Mar 2023 17:08:41 +0100 Subject: [PATCH 11/17] Fix lambda having modify access - not needed --- src/libslic3r/PrintObject.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 2bd19ba5a..68ddb7ba9 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2021,11 +2021,11 @@ void PrintObject::bridge_over_infill() return expanded_bridged_area; }; - tbb::parallel_for(tbb::blocked_range(0, clustered_layers_for_threads.size()), [po = this, &surfaces_by_layer, - &clustered_layers_for_threads, - &gather_areas_w_depth, &infill_lines, - &determine_bridging_angle, - &construct_anchored_polygon]( + tbb::parallel_for(tbb::blocked_range(0, clustered_layers_for_threads.size()), [po = static_cast(this), + &surfaces_by_layer, &clustered_layers_for_threads, + gather_areas_w_depth, &infill_lines, + determine_bridging_angle, + construct_anchored_polygon]( tbb::blocked_range r) { for (size_t cluster_idx = r.begin(); cluster_idx < r.end(); cluster_idx++) { for (size_t job_idx = 0; job_idx < clustered_layers_for_threads[cluster_idx].size(); job_idx++) { From 4af37ca4a52c812f2b4fa77f2833d2e02c43e44d Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 14 Mar 2023 10:43:31 +0100 Subject: [PATCH 12/17] fix bug - layer id is not same as layer idx, because of raft (super confusing) --- src/libslic3r/PrintObject.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 68ddb7ba9..5efad85b7 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1586,17 +1586,20 @@ void PrintObject::bridge_over_infill() struct CandidateSurface { CandidateSurface(const Surface *original_surface, + int layer_index, Polygons new_polys, const LayerRegion *region, double bridge_angle, bool supported_by_lightning) : original_surface(original_surface) + , layer_index(layer_index) , new_polys(new_polys) , region(region) , bridge_angle(bridge_angle) , supported_by_lightning(supported_by_lightning) {} const Surface *original_surface; + int layer_index; Polygons new_polys; const LayerRegion *region; double bridge_angle; @@ -1644,10 +1647,10 @@ void PrintObject::bridge_over_infill() bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON; if (!unsupported.empty() && (!partially_supported || area(unsupported) > 5 * 5 * spacing * spacing)) { Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); - candidate_surfaces.push_back(CandidateSurface(s, worth_bridging, region, 0, contains_only_lightning)); + candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0, contains_only_lightning)); #ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(region->layer()->id()) + "_candidate_surface_" + std::to_string(area(s->expolygon)), + debug_draw(std::to_string(lidx) + "_candidate_surface_" + std::to_string(area(s->expolygon)), to_lines(region->layer()->lslices), to_lines(s->expolygon), to_lines(worth_bridging), to_lines(unsupported_area)); #endif @@ -1658,7 +1661,7 @@ void PrintObject::bridge_over_infill() }); for (const CandidateSurface &c : candidate_surfaces) { - surfaces_by_layer[c.region->layer()->id()].push_back(c); + surfaces_by_layer[c.layer_index].push_back(c); } } @@ -2053,7 +2056,7 @@ void PrintObject::bridge_over_infill() // Now also remove area that has been already filled on lower layers by bridging expansion - For this // reason we did the clustering of layers per thread. - double bottom_z = po->get_layer(lidx)->print_z - thick_bridges_depth - EPSILON; + double bottom_z = layer->print_z - thick_bridges_depth - EPSILON; if (job_idx > 0) { for (int lower_job_idx = job_idx - 1; lower_job_idx >= 0; lower_job_idx--) { size_t lower_layer_idx = clustered_layers_for_threads[cluster_idx][lower_job_idx]; @@ -2124,8 +2127,8 @@ void PrintObject::bridge_over_infill() to_lines(bridging_area)); #endif - expanded_surfaces.push_back( - CandidateSurface(candidate.original_surface, bridging_area, candidate.region, bridging_angle, candidate.supported_by_lightning)); + expanded_surfaces.push_back(CandidateSurface(candidate.original_surface, candidate.layer_index, bridging_area, + candidate.region, bridging_angle, candidate.supported_by_lightning)); } surfaces_by_layer[lidx].swap(expanded_surfaces); expanded_surfaces.clear(); From 257837c07176e416a72bd1b58ae37bda7170922d Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 14 Mar 2023 11:38:16 +0100 Subject: [PATCH 13/17] Fix one and zero perimeter case - no bridging was generated --- src/libslic3r/PrintObject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5efad85b7..8dbf96b26 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2090,7 +2090,7 @@ void PrintObject::bridge_over_infill() if (area_to_be_bridge.empty()) continue; - Polygons boundary_area = union_(expansion_area, expand(area_to_be_bridge, flow.scaled_spacing())); + Polygons boundary_area = union_(expansion_area, area_to_be_bridge); Polylines boundary_plines = to_polylines(boundary_area); double bridging_angle = 0; Polygons tmp_expanded_area = expand(area_to_be_bridge, 3.0 * flow.scaled_spacing()); @@ -2118,7 +2118,7 @@ void PrintObject::bridge_over_infill() Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle); bridging_area = intersection(bridging_area, boundary_area); bridging_area = opening(bridging_area, flow.scaled_spacing()); - expansion_area = diff(expansion_area, bridging_area); + expansion_area = diff(expansion_area, bridging_area); #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + From c6fd339a39b0b7279eadf44936f7c26098ccec7c Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 14 Mar 2023 12:12:18 +0100 Subject: [PATCH 14/17] remove cracks in boundary surface via closing - without it, the bridges were sometimes cut in half --- src/libslic3r/PrintObject.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 8dbf96b26..b16dea179 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2090,10 +2090,11 @@ void PrintObject::bridge_over_infill() if (area_to_be_bridge.empty()) continue; - Polygons boundary_area = union_(expansion_area, area_to_be_bridge); - Polylines boundary_plines = to_polylines(boundary_area); - double bridging_angle = 0; - Polygons tmp_expanded_area = expand(area_to_be_bridge, 3.0 * flow.scaled_spacing()); + Polygons boundary_area = union_(expansion_area, area_to_be_bridge); + boundary_area = closing(boundary_area, 0.3 * flow.scaled_spacing()); + Polylines boundary_plines = to_polylines(boundary_area); + double bridging_angle = 0; + Polygons tmp_expanded_area = expand(area_to_be_bridge, 3.0 * flow.scaled_spacing()); for (const CandidateSurface &s : expanded_surfaces) { if (!intersection(s.new_polys, tmp_expanded_area).empty()) { bridging_angle = s.bridge_angle; From eb73bf7f24b94c3b0b273026444c51f2b96078d9 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 14 Mar 2023 16:40:55 +0100 Subject: [PATCH 15/17] Bridging over sparse infill - improve coliding regions merging, smoothen results, dissolve tiny ensuring regions around bridging --- src/libslic3r/Print.cpp | 2 +- src/libslic3r/PrintObject.cpp | 110 +++++++++++++++++++--------------- 2 files changed, 64 insertions(+), 48 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index a3eba0148..370fa3ba2 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1185,7 +1185,7 @@ void Print::alert_when_supports_needed() case SupportSpotsGenerator::SupportPointCause::WeakObjectPart: message = L("thin fragile section"); break; } - return (critical ? "!" : "") + message; + return message; }; // vector of pairs of object and its issues, where each issue is a pair of type and critical flag diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index b16dea179..ccdc7a914 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1647,6 +1647,12 @@ void PrintObject::bridge_over_infill() bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON; if (!unsupported.empty() && (!partially_supported || area(unsupported) > 5 * 5 * spacing * spacing)) { Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); + for (Polygon p : diff(to_polygons(s->expolygon), worth_bridging)) { + if (p.area() < region->flow(frSolidInfill, true).scaled_spacing() * scale_(12.0)) { + worth_bridging.push_back(p); + } + } + worth_bridging = intersection(closing(worth_bridging, 3 * region->flow(frSolidInfill, true).scaled_spacing()), s->expolygon); candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0, contains_only_lightning)); #ifdef DEBUG_BRIDGE_OVER_INFILL @@ -1705,6 +1711,7 @@ void PrintObject::bridge_over_infill() // cluster layers by depth needed for thick bridges. Each cluster is to be processed by single thread sequentially, so that bridges cannot appear one on another std::vector> clustered_layers_for_threads; + float target_flow_height_factor = 0.5; { std::vector layers_with_candidates; std::map layer_area_covered_by_candidates; @@ -1732,7 +1739,8 @@ void PrintObject::bridge_over_infill() for (auto pair : surfaces_by_layer) { if (clustered_layers_for_threads.empty() || this->get_layer(clustered_layers_for_threads.back().back())->print_z < - this->get_layer(pair.first)->print_z - this->get_layer(pair.first)->regions()[0]->flow(frSolidInfill, true).height() - + this->get_layer(pair.first)->print_z - + this->get_layer(pair.first)->regions()[0]->flow(frSolidInfill, true).height() * target_flow_height_factor - EPSILON || intersection(layer_area_covered_by_candidates[clustered_layers_for_threads.back().back()], layer_area_covered_by_candidates[pair.first]) @@ -1756,35 +1764,34 @@ void PrintObject::bridge_over_infill() } // LAMBDA to gather areas with sparse infill deep enough that we can fit thick bridges there. - auto gather_areas_w_depth = - [](const PrintObject *po, int lidx, float target_flow_height) { - // Gather lower layers sparse infill areas, to depth defined by used bridge flow - Polygons lower_layers_sparse_infill{}; - Polygons not_sparse_infill{}; - double bottom_z = po->get_layer(lidx)->print_z - target_flow_height - EPSILON; - for (int i = int(lidx) - 1; i >= 0; --i) { - // Stop iterating if layer is lower than bottom_z. - const Layer *layer = po->get_layer(i); - if (layer->print_z < bottom_z) - break; + auto gather_areas_w_depth = [target_flow_height_factor](const PrintObject *po, int lidx, float target_flow_height) { + // Gather lower layers sparse infill areas, to depth defined by used bridge flow + Polygons lower_layers_sparse_infill{}; + Polygons not_sparse_infill{}; + double bottom_z = po->get_layer(lidx)->print_z - target_flow_height * target_flow_height_factor - EPSILON; + for (int i = int(lidx) - 1; i >= 0; --i) { + // Stop iterating if layer is lower than bottom_z. + const Layer *layer = po->get_layer(i); + if (layer->print_z < bottom_z) + break; - for (const LayerRegion *region : layer->regions()) { - bool has_low_density = region->region().config().fill_density.value < 100; - for (const Surface &surface : region->fill_surfaces()) { - if (surface.surface_type == stInternal && has_low_density) { - Polygons p = to_polygons(surface.expolygon); - lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), p.begin(), p.end()); - } else { - Polygons p = to_polygons(surface.expolygon); - not_sparse_infill.insert(not_sparse_infill.end(), p.begin(), p.end()); - } + for (const LayerRegion *region : layer->regions()) { + bool has_low_density = region->region().config().fill_density.value < 100; + for (const Surface &surface : region->fill_surfaces()) { + if (surface.surface_type == stInternal && has_low_density) { + Polygons p = to_polygons(surface.expolygon); + lower_layers_sparse_infill.insert(lower_layers_sparse_infill.end(), p.begin(), p.end()); + } else { + Polygons p = to_polygons(surface.expolygon); + not_sparse_infill.insert(not_sparse_infill.end(), p.begin(), p.end()); } } - lower_layers_sparse_infill = union_(lower_layers_sparse_infill); } + lower_layers_sparse_infill = union_(lower_layers_sparse_infill); + } - return diff(lower_layers_sparse_infill, not_sparse_infill); - }; + return diff(lower_layers_sparse_infill, not_sparse_infill); + }; // LAMBDA do determine optimal bridging angle auto determine_bridging_angle = [](const Polygons &bridged_area, const Lines &anchors, InfillPattern dominant_pattern) { @@ -2052,7 +2059,7 @@ void PrintObject::bridge_over_infill() // Gather deep infill areas, where thick bridges fit coordf_t thick_bridges_depth = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).height(); - Polygons deep_infill_area = gather_areas_w_depth(po, lidx, thick_bridges_depth); + Polygons deep_infill_area = gather_areas_w_depth(po, lidx, thick_bridges_depth * 0.5); // Now also remove area that has been already filled on lower layers by bridging expansion - For this // reason we did the clustering of layers per thread. @@ -2090,26 +2097,18 @@ void PrintObject::bridge_over_infill() if (area_to_be_bridge.empty()) continue; - Polygons boundary_area = union_(expansion_area, area_to_be_bridge); - boundary_area = closing(boundary_area, 0.3 * flow.scaled_spacing()); - Polylines boundary_plines = to_polylines(boundary_area); - double bridging_angle = 0; - Polygons tmp_expanded_area = expand(area_to_be_bridge, 3.0 * flow.scaled_spacing()); - for (const CandidateSurface &s : expanded_surfaces) { - if (!intersection(s.new_polys, tmp_expanded_area).empty()) { - bridging_angle = s.bridge_angle; - break; - } - } - if (bridging_angle == 0) { - if (!anchors.empty()) { - bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors), - candidate.region->region().config().fill_pattern.value); - } else { - // use expansion boundaries as anchors. - // Also, use Infill pattern that is neutral for angle determination, since there are no infill lines. - bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(boundary_plines), InfillPattern::ipLine); - } + area_to_be_bridge = expand(area_to_be_bridge, flow.scaled_spacing()); + Polygons boundary_area = union_(expansion_area, area_to_be_bridge); + Polylines boundary_plines = to_polylines(boundary_area); + + double bridging_angle = 0; + if (!anchors.empty()) { + bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors), + candidate.region->region().config().fill_pattern.value); + } else { + // use expansion boundaries as anchors. + // Also, use Infill pattern that is neutral for angle determination, since there are no infill lines. + bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(boundary_plines), InfillPattern::ipLine); } boundary_plines.insert(boundary_plines.end(), anchors.begin(), anchors.end()); @@ -2117,6 +2116,23 @@ void PrintObject::bridge_over_infill() boundary_plines = intersection_pl(boundary_plines, expand(area_to_be_bridge, scale_(10))); } Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle); + + // Check collision with other expanded surfaces + { + bool reconstruct = false; + Polygons tmp_expanded_area = expand(bridging_area, 3.0 * flow.scaled_spacing()); + for (const CandidateSurface &s : expanded_surfaces) { + if (!intersection(s.new_polys, tmp_expanded_area).empty()) { + bridging_angle = s.bridge_angle; + reconstruct = true; + break; + } + } + if (reconstruct) { + bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle); + } + } + bridging_area = intersection(bridging_area, boundary_area); bridging_area = opening(bridging_area, flow.scaled_spacing()); expansion_area = diff(expansion_area, bridging_area); @@ -2124,7 +2140,7 @@ void PrintObject::bridge_over_infill() #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_expanded_bridging", - to_lines(layer->lslices), to_lines(candidate.original_surface->expolygon), to_lines(candidate.new_polys), + to_lines(layer->lslices), to_lines(boundary_plines), to_lines(candidate.new_polys), to_lines(bridging_area)); #endif From f2f9b890961c6f8b0b3b78e22694ceebd962491e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 14 Mar 2023 19:32:27 +0100 Subject: [PATCH 16/17] Fix of #9963 Bridge angle not accept degree but rad Fixes SPE-1583 --- src/libslic3r/LayerRegion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 6017f393e..073907c8c 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -378,7 +378,7 @@ 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_merge_surfaces(m_fill_surfaces.surfaces, stBottomBridge, shells, params, custom_angle) : + expand_merge_surfaces(m_fill_surfaces.surfaces, stBottomBridge, shells, params, Geometry::deg2rad(custom_angle)) : expand_bridges_detect_orientations(m_fill_surfaces.surfaces, shells, params); BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done"; #if 0 From c585aaa1cbd75c4cd05ff453e4eba6b4eb4b6ec7 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 14 Mar 2023 19:33:24 +0100 Subject: [PATCH 17/17] TriangleMeshSlicer.cpp: Fixed compilation of debug output --- src/libslic3r/TriangleMeshSlicer.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index b91d1559f..7faa79435 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -34,6 +34,10 @@ // #define SLIC3R_DEBUG_SLICE_PROCESSING +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + #define DEBUG_INTERSECTIONLINE +#endif + #if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING) #include "SVG.hpp" #endif @@ -125,7 +129,7 @@ public: }; uint32_t flags { 0 }; -#if DEBUG_INTERSECTIONLINE +#ifdef DEBUG_INTERSECTIONLINE enum class Source { BottomPlane, TopPlane, @@ -1446,19 +1450,19 @@ static std::vector make_slab_loops( for (const IntersectionLine &l : lines.at_slice[slice_below]) if (l.edge_type != IntersectionLine::FacetEdgeType::Top) { in.emplace_back(l); -#if DEBUG_INTERSECTIONLINE +#ifdef DEBUG_INTERSECTIONLINE in.back().source = IntersectionLine::Source::BottomPlane; #endif // DEBUG_INTERSECTIONLINE } } { // Edges in between slice_below and slice_above. -#if DEBUG_INTERSECTIONLINE +#ifdef DEBUG_INTERSECTIONLINE size_t old_size = in.size(); #endif // DEBUG_INTERSECTIONLINE // Edge IDs of end points on in-between lines that touch the layer above are already increased with num_edges. append(in, lines.between_slices[line_idx]); -#if DEBUG_INTERSECTIONLINE +#ifdef DEBUG_INTERSECTIONLINE for (auto it = in.begin() + old_size; it != in.end(); ++ it) { assert(it->edge_type == IntersectionLine::FacetEdgeType::Slab); it->source = IntersectionLine::Source::Slab; @@ -1476,7 +1480,7 @@ static std::vector make_slab_loops( l.edge_a_id += num_edges; if (l.edge_b_id >= 0) l.edge_b_id += num_edges; -#if DEBUG_INTERSECTIONLINE +#ifdef DEBUG_INTERSECTIONLINE l.source = IntersectionLine::Source::TopPlane; #endif // DEBUG_INTERSECTIONLINE }