From adf39805bce53ca896b24690703879229f946ee7 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 4 Apr 2022 12:35:29 +0200 Subject: [PATCH] work in progress: hooked in new step: posSupportableIssuesSearch created layout of the processing --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Print.cpp | 2 + src/libslic3r/Print.hpp | 3 +- src/libslic3r/PrintObject.cpp | 150 +++++++++++++--------- src/libslic3r/SupportableIssuesSearch.cpp | 149 +++++++++++++++++++++ src/libslic3r/SupportableIssuesSearch.hpp | 16 +++ 6 files changed, 257 insertions(+), 65 deletions(-) create mode 100644 src/libslic3r/SupportableIssuesSearch.cpp create mode 100644 src/libslic3r/SupportableIssuesSearch.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 01a6a3aa2a..a342da8319 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -245,6 +245,8 @@ set(SLIC3R_SOURCES SlicingAdaptive.hpp Subdivide.cpp Subdivide.hpp + SupportableIssuesSearch.cpp + SupportableIssuesSearch.hpp SupportMaterial.cpp SupportMaterial.hpp Surface.cpp diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 9792a6968b..a50974cafd 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -825,6 +825,8 @@ void Print::process() obj->infill(); for (PrintObject *obj : m_objects) obj->ironing(); + for (PrintObject *obj : m_objects) + obj->find_supportable_issues(); for (PrintObject *obj : m_objects) obj->generate_support_material(); if (this->set_started(psWipeTower)) { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index cb01600fa5..c89b463a8f 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -61,7 +61,7 @@ enum PrintStep : unsigned int { enum PrintObjectStep : unsigned int { posSlice, posPerimeters, posPrepareInfill, - posInfill, posIroning, posSupportMaterial, posCount, + posInfill, posIroning, posSupportableIssuesSearch, posSupportMaterial, posCount, }; // A PrintRegion object represents a group of volumes to print @@ -381,6 +381,7 @@ private: void prepare_infill(); void infill(); void ironing(); + void find_supportable_issues(); void generate_support_material(); void slice_volumes(); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 6ec27ea95b..4bcf74fe5e 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -16,6 +16,7 @@ #include "Fill/FillAdaptive.hpp" #include "Fill/FillLightning.hpp" #include "Format/STL.hpp" +#include "SupportableIssuesSearch.hpp" #include #include @@ -89,7 +90,7 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances) // Invalidate and set copies. PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED; bool equal_length = instances.size() == m_instances.size(); - bool equal = equal_length && std::equal(instances.begin(), instances.end(), m_instances.begin(), + bool equal = equal_length && std::equal(instances.begin(), instances.end(), m_instances.begin(), [](const PrintInstance& lhs, const PrintInstance& rhs) { return lhs.model_instance == rhs.model_instance && lhs.shift == rhs.shift; }); if (! equal) { status = PrintBase::APPLY_STATUS_CHANGED; @@ -125,7 +126,7 @@ void PrintObject::make_perimeters() m_print->set_status(20, L("Generating perimeters")); BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); - + // Revert the typed slices into untyped slices. if (m_typed_slices) { for (Layer *layer : m_layers) { @@ -134,10 +135,10 @@ void PrintObject::make_perimeters() } m_typed_slices = false; } - + // compare each layer to the one below, and mark those slices needing // one additional inner perimeter, like the top of domed objects- - + // this algorithm makes sure that at least one perimeter is overlapping // but we don't generate any extra perimeter if fill density is zero, as they would be floating // inside the object - infill_only_where_needed should be the method of choice for printing @@ -245,7 +246,7 @@ void PrintObject::prepare_infill() // by the cummulative area of the previous $layerm->fill_surfaces. this->detect_surfaces_type(); m_print->throw_if_canceled(); - + // Decide what surfaces are to be filled. // Here the stTop / stBottomBridge / stBottom infill is turned to just stInternal if zero top / bottom infill layers are configured. // Also tiny stInternal surfaces are turned to stInternalSolid. @@ -320,7 +321,7 @@ void PrintObject::prepare_infill() } // for each layer } // for each region #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - + // the following step needs to be done before combination because it may need // to remove only half of the combined infill this->bridge_over_infill(); @@ -395,12 +396,33 @@ void PrintObject::ironing() } } +void PrintObject::find_supportable_issues() +{ + if (this->set_started(posSupportableIssuesSearch)) { + BOOST_LOG_TRIVIAL(debug) + << "Searching supportable issues - start"; + //TODO status number? + m_print->set_status(70, L("Searching supportable issues")); + + if (this->has_support()) { + + } else { + SupportableIssues::quick_search(this); + } + + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) + << "Searching supportable issues - end"; + this->set_done(posSupportableIssuesSearch); + } +} + void PrintObject::generate_support_material() { if (this->set_started(posSupportMaterial)) { this->clear_support_layers(); if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && ! m_layers.empty())) { - m_print->set_status(85, L("Generating support material")); + m_print->set_status(85, L("Generating support material")); this->_generate_support_material(); m_print->throw_if_canceled(); } else { @@ -560,7 +582,7 @@ bool PrintObject::invalidate_state_by_config_options( } else if ( opt_key == "clip_multipart_objects" || opt_key == "elefant_foot_compensation" - || opt_key == "support_material_contact_distance" + || opt_key == "support_material_contact_distance" || opt_key == "xy_size_compensation") { steps.emplace_back(posSlice); } else if (opt_key == "support_material") { @@ -711,7 +733,7 @@ bool PrintObject::invalidate_state_by_config_options( bool PrintObject::invalidate_step(PrintObjectStep step) { bool invalidated = Inherited::invalidate_step(step); - + // propagate to dependent steps if (step == posPerimeters) { invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill, posIroning }); @@ -784,7 +806,7 @@ void PrintObject::detect_surfaces_type() surfaces_new.assign(num_layers, Surfaces()); tbb::parallel_for( - tbb::blocked_range(0, + tbb::blocked_range(0, spiral_vase ? // In spiral vase mode, reserve the last layer for the top surface if more than 1 layer is planned for the vase bottom. ((num_layers > 1) ? num_layers - 1 : num_layers) : @@ -812,7 +834,7 @@ void PrintObject::detect_surfaces_type() // of current layer and upper one) Surfaces top; if (upper_layer) { - ExPolygons upper_slices = interface_shells ? + ExPolygons upper_slices = interface_shells ? diff_ex(layerm->slices.surfaces, upper_layer->m_regions[region_id]->slices.surfaces, ApplySafetyOffset::Yes) : diff_ex(layerm->slices.surfaces, upper_layer->lslices, ApplySafetyOffset::Yes); surfaces_append(top, opening_ex(upper_slices, offset), stTop); @@ -823,7 +845,7 @@ void PrintObject::detect_surfaces_type() for (Surface &surface : top) surface.surface_type = stTop; } - + // Find bottom surfaces (difference between current surfaces of current layer and lower one). Surfaces bottom; if (lower_layer) { @@ -846,7 +868,7 @@ void PrintObject::detect_surfaces_type() // if user requested internal shells, we need to identify surfaces // lying on other slices not belonging to this region if (interface_shells) { - // non-bridging bottom surfaces: any part of this layer lying + // non-bridging bottom surfaces: any part of this layer lying // on something else, excluding those lying on our own region surfaces_append( bottom, @@ -866,7 +888,7 @@ void PrintObject::detect_surfaces_type() for (Surface &surface : bottom) surface.surface_type = stBottom; } - + // now, if the object contained a thin membrane, we could have overlapping bottom // and top surfaces; let's do an intersection to discover them and consider them // as bottom surfaces (to allow for bridge detection) @@ -889,7 +911,7 @@ void PrintObject::detect_surfaces_type() SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, region_id, layer->print_z).c_str(), expolygons_with_attributes); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - + // save surfaces to layer Surfaces &surfaces_out = interface_shells ? surfaces_new[idx_layer] : layerm->slices.surfaces; Surfaces surfaces_backup; @@ -908,7 +930,7 @@ void PrintObject::detect_surfaces_type() surfaces_append(surfaces_out, std::move(top)); surfaces_append(surfaces_out, std::move(bottom)); - + // Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n", // $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug; @@ -1216,7 +1238,7 @@ void PrintObject::discover_vertical_shells() #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ Flow solid_infill_flow = layerm->flow(frSolidInfill); - coord_t infill_line_spacing = solid_infill_flow.scaled_spacing(); + coord_t infill_line_spacing = solid_infill_flow.scaled_spacing(); // Find a union of perimeters below / above this surface to guarantee a minimum shell thickness. Polygons shell; Polygons holes; @@ -1252,8 +1274,8 @@ void PrintObject::discover_vertical_shells() if (int n_top_layers = region_config.top_solid_layers.value; n_top_layers > 0) { // Gather top regions projected to this layer. coordf_t print_z = layer->print_z; - for (int i = int(idx_layer) + 1; - i < int(cache_top_botom_regions.size()) && + for (int i = int(idx_layer) + 1; + i < int(cache_top_botom_regions.size()) && (i < int(idx_layer) + n_top_layers || m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON); ++ i) { @@ -1262,7 +1284,7 @@ void PrintObject::discover_vertical_shells() holes = intersection(holes, cache.holes); if (! cache.top_surfaces.empty()) { polygons_append(shell, cache.top_surfaces); - // Running the union_ using the Clipper library piece by piece is cheaper + // Running the union_ using the Clipper library piece by piece is cheaper // than running the union_ all at once. shell = union_(shell); } @@ -1281,7 +1303,7 @@ void PrintObject::discover_vertical_shells() holes = intersection(holes, cache.holes); if (! cache.bottom_surfaces.empty()) { polygons_append(shell, cache.bottom_surfaces); - // Running the union_ using the Clipper library piece by piece is cheaper + // Running the union_ using the Clipper library piece by piece is cheaper // than running the union_ all at once. shell = union_(shell); } @@ -1292,14 +1314,14 @@ void PrintObject::discover_vertical_shells() Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); svg.draw(shell); svg.draw_outline(shell, "black", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #if 0 { PROFILE_BLOCK(discover_vertical_shells_region_layer_shell_); // shell = union_(shell, true); - shell = union_(shell, false); + shell = union_(shell, false); } #endif #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -1315,7 +1337,7 @@ void PrintObject::discover_vertical_shells() Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", debug_idx), get_extents(shell)); svg.draw(shell_ex); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ @@ -1327,7 +1349,7 @@ void PrintObject::discover_vertical_shells() svg.draw(shell_ex, "blue", 0.5); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); - } + } { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); @@ -1335,15 +1357,15 @@ void PrintObject::discover_vertical_shells() svg.draw(shell_ex, "blue", 0.5); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); - } + } { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05)); svg.draw(shell_ex, "blue", 0.5); - svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); + svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); - } + } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ // Trim the shells region by the internal & internal void surfaces. @@ -1364,7 +1386,7 @@ void PrintObject::discover_vertical_shells() Polygons shell_before = shell; #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #if 1 - // Intentionally inflate a bit more than how much the region has been shrunk, + // Intentionally inflate a bit more than how much the region has been shrunk, // so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill). shell = opening(union_(shell), 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare); if (shell.empty()) @@ -1446,7 +1468,7 @@ void PrintObject::bridge_over_infill() for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { const PrintRegion ®ion = this->printing_region(region_id); - + // skip bridging in case there are no voids if (region.config().fill_density.value == 100) continue; @@ -1455,7 +1477,7 @@ void PrintObject::bridge_over_infill() // skip first layer if (layer_it == m_layers.begin()) continue; - + Layer *layer = *layer_it; LayerRegion *layerm = layer->m_regions[region_id]; Flow bridge_flow = layerm->bridging_flow(frSolidInfill); @@ -1463,31 +1485,31 @@ void PrintObject::bridge_over_infill() // extract the stInternalSolid surfaces that might be transformed into bridges Polygons internal_solid; layerm->fill_surfaces.filter_by_type(stInternalSolid, &internal_solid); - + // check whether the lower area is deep enough for absorbing the extra flow // (for obvious physical reasons but also for preventing the bridge extrudates // from overflowing in 3D preview) ExPolygons to_bridge; { Polygons to_bridge_pp = internal_solid; - + // iterate through lower layers spanned by bridge_flow double bottom_z = layer->print_z - bridge_flow.height() - EPSILON; for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) { const Layer* lower_layer = m_layers[i]; - + // stop iterating if layer is lower than bottom_z if (lower_layer->print_z < bottom_z) break; - + // iterate through regions and collect internal surfaces Polygons lower_internal; for (LayerRegion *lower_layerm : lower_layer->m_regions) lower_layerm->fill_surfaces.filter_by_type(stInternal, &lower_internal); - + // intersect such lower internal surfaces with the candidate solid surfaces to_bridge_pp = intersection(to_bridge_pp, lower_internal); } - + // there's no point in bridging too thin/short regions //FIXME Vojtech: The offset2 function is not a geometric offset, // therefore it may create 1) gaps, and 2) sharp corners, which are outside the original contour. @@ -1496,17 +1518,17 @@ void PrintObject::bridge_over_infill() float min_width = float(bridge_flow.scaled_width()) * 3.f; to_bridge_pp = opening(to_bridge_pp, min_width); } - + if (to_bridge_pp.empty()) continue; - + // convert into ExPolygons to_bridge = union_ex(to_bridge_pp); } - + #ifdef SLIC3R_DEBUG printf("Bridging %zu internal areas at layer %zu\n", to_bridge.size(), layer->id()); #endif - + // compute the remaning internal solid surfaces as difference ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, ApplySafetyOffset::Yes); to_bridge = intersection_ex(to_bridge, internal_solid, ApplySafetyOffset::Yes); @@ -1515,7 +1537,7 @@ void PrintObject::bridge_over_infill() for (ExPolygon &ex : to_bridge) layerm->fill_surfaces.surfaces.push_back(Surface(stInternalBridge, ex)); for (ExPolygon &ex : not_to_bridge) - layerm->fill_surfaces.surfaces.push_back(Surface(stInternalSolid, ex)); + layerm->fill_surfaces.surfaces.push_back(Surface(stInternalSolid, ex)); /* # exclude infill from the layers below if needed # see discussion at https://github.com/alexrj/Slic3r/issues/240 @@ -1544,7 +1566,7 @@ void PrintObject::bridge_over_infill() $lower_layerm->fill_surfaces->clear; $lower_layerm->fill_surfaces->append($_) for @new_surfaces; } - + $excess -= $self->get_layer($i)->height; } } @@ -1634,7 +1656,7 @@ PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &defau // Switch of infill for very low infill rates, also avoid division by zero in infill generator for these very low rates. // See GH issue #5910. config.fill_density.value = 0; - else + else config.fill_density.value = std::min(config.fill_density.value, 100.); if (config.fuzzy_skin.value != FuzzySkinType::None && (config.fuzzy_skin_point_dist.value < 0.01 || config.fuzzy_skin_thickness.value < 0.001)) config.fuzzy_skin.value = FuzzySkinType::None; @@ -1793,7 +1815,7 @@ void PrintObject::clip_fill_surfaces() // Regularize the overhang regions, so that the infill areas will not become excessively jagged. smooth_outward( closing(upper_internal, closing_radius, ClipperLib::jtSquare, 0.), - scaled(0.1)), + scaled(0.1)), lower_layer_internal_surfaces); // Apply new internal infill to regions. for (LayerRegion *layerm : lower_layer->m_regions) { @@ -1821,7 +1843,7 @@ void PrintObject::clip_fill_surfaces() void PrintObject::discover_horizontal_shells() { BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()"; - + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { for (size_t i = 0; i < m_layers.size(); ++ i) { m_print->throw_if_canceled(); @@ -1840,7 +1862,7 @@ void PrintObject::discover_horizontal_shells() // If ensure_vertical_shell_thickness, then the rest has already been performed by discover_vertical_shells(). if (region_config.ensure_vertical_shell_thickness.value) continue; - + coordf_t print_z = layer->print_z; coordf_t bottom_z = layer->bottom_z(); for (size_t idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) { @@ -1873,11 +1895,11 @@ void PrintObject::discover_horizontal_shells() if (solid.empty()) continue; // Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == stTop) ? 'top' : 'bottom'; - + // Scatter top / bottom regions to other layers. Scattering process is inherently serial, it is difficult to parallelize without locking. for (int n = (type == stTop) ? int(i) - 1 : int(i) + 1; (type == stTop) ? - (n >= 0 && (int(i) - n < num_solid_layers || + (n >= 0 && (int(i) - n < num_solid_layers || print_z - m_layers[n]->print_z < region_config.top_solid_min_thickness.value - EPSILON)) : (n < int(m_layers.size()) && (n - int(i) < num_solid_layers || m_layers[n]->bottom_z() - bottom_z < region_config.bottom_solid_min_thickness.value - EPSILON)); @@ -1886,7 +1908,7 @@ void PrintObject::discover_horizontal_shells() // Slic3r::debugf " looking for neighbors on layer %d...\n", $n; // Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface. LayerRegion *neighbor_layerm = m_layers[n]->regions()[region_id]; - + // find intersection between neighbor and current layer's surfaces // intersections have contours and holes // we update $solid so that we limit the next neighbor layer to the areas that were @@ -1921,7 +1943,7 @@ void PrintObject::discover_horizontal_shells() continue; } } - + if (region_config.fill_density.value == 0) { // if we're printing a hollow object we discard any solid shell thinner // than a perimeter width, since it's probably just crossing a sloping wall @@ -1929,7 +1951,7 @@ void PrintObject::discover_horizontal_shells() // obeying the solid shell count option strictly (DWIM!) float margin = float(neighbor_layerm->flow(frExternalPerimeter).scaled_width()); Polygons too_narrow = diff( - new_internal_solid, + new_internal_solid, opening(new_internal_solid, margin, margin + ClipperSafetyOffset, jtMiter, 5)); // Trim the regularized region by the original region. if (! too_narrow.empty()) @@ -1959,20 +1981,20 @@ void PrintObject::discover_horizontal_shells() for (const Surface &surface : neighbor_layerm->fill_surfaces.surfaces) if (surface.is_internal() && !surface.is_bridge()) polygons_append(internal, to_polygons(surface.expolygon)); - polygons_append(new_internal_solid, + polygons_append(new_internal_solid, intersection( expand(too_narrow, +margin), // Discard bridges as they are grown for anchoring and we can't - // remove such anchors. (This may happen when a bridge is being + // remove such anchors. (This may happen when a bridge is being // anchored onto a wall where little space remains after the bridge - // is grown, and that little space is an internal solid shell so + // is grown, and that little space is an internal solid shell so // it triggers this too_narrow logic.) internal)); // see https://github.com/prusa3d/PrusaSlicer/pull/3426 // solid = new_internal_solid; } } - + // internal-solid are the union of the existing internal-solid surfaces // and new ones SurfaceCollection backup = std::move(neighbor_layerm->fill_surfaces); @@ -2051,11 +2073,11 @@ void PrintObject::combine_infill() current_height += layer->height; ++ num_layers; } - + // Append lower layers (if any) to uppermost layer. combine[m_layers.size() - 1] = num_layers; } - + // loop through layers to which we have assigned layers to combine for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) { m_print->throw_if_canceled(); @@ -2075,8 +2097,8 @@ void PrintObject::combine_infill() intersection = intersection_ex(layerms[i]->fill_surfaces.filter_by_type(stInternal), intersection); double area_threshold = layerms.front()->infill_area_threshold(); if (! intersection.empty() && area_threshold > 0.) - intersection.erase(std::remove_if(intersection.begin(), intersection.end(), - [area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }), + intersection.erase(std::remove_if(intersection.begin(), intersection.end(), + [area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }), intersection.end()); if (intersection.empty()) continue; @@ -2088,15 +2110,15 @@ void PrintObject::combine_infill() // so let's remove those areas from all layers. Polygons intersection_with_clearance; intersection_with_clearance.reserve(intersection.size()); - float clearance_offset = + float clearance_offset = 0.5f * layerms.back()->flow(frPerimeter).scaled_width() + - // Because fill areas for rectilinear and honeycomb are grown + // Because fill areas for rectilinear and honeycomb are grown // later to overlap perimeters, we need to counteract that too. ((region.config().fill_pattern == ipRectilinear || region.config().fill_pattern == ipMonotonic || region.config().fill_pattern == ipGrid || region.config().fill_pattern == ipLine || - region.config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * + region.config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * layerms.back()->flow(frSolidInfill).scaled_width(); for (ExPolygon &expoly : intersection) polygons_append(intersection_with_clearance, offset(expoly, clearance_offset)); @@ -2308,7 +2330,7 @@ static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const index // The resulting triangles are fed to the Clipper library, which seem to handle flipped triangles well. // if (cross2(Vec2d((poly.pts[1] - poly.pts[0]).cast()), Vec2d((poly.pts[2] - poly.pts[1]).cast())) < 0) // std::swap(poly.pts.front(), poly.pts.back()); - + out[layer_id].emplace_back(std::move(poly.pts)); ++layer_id; } diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp new file mode 100644 index 0000000000..bace1aea00 --- /dev/null +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -0,0 +1,149 @@ +#include "SupportableIssuesSearch.hpp" + +#include "tbb/parallel_for.h" +#include "tbb/blocked_range.h" +#include "tbb/parallel_reduce.h" +#include + +#include "libslic3r/Layer.hpp" +#include "libslic3r/EdgeGrid.hpp" +#include "libslic3r/ClipperUtils.hpp" + +namespace Slic3r { +namespace SupportableIssues { + +struct Params { + float bridge_distance = 5.0f; + float printable_protrusion_distance = 1.0f; +}; + +namespace Impl { + +struct LayerDescriptor { + Vec2f centroid { 0.0f, 0.0f }; + size_t segments_count { 0 }; + float perimeter_length { 0.0f }; +}; + +struct EdgeGridWrapper { + EdgeGridWrapper(coord_t resolution, ExPolygons ex_polys) : + ex_polys(ex_polys) { + + grid.create(this->ex_polys, resolution); + grid.calculate_sdf(); + } + EdgeGrid::Grid grid; + ExPolygons ex_polys; +} +; + +EdgeGridWrapper compute_layer_merged_edge_grid(const Layer *layer) { + static const float eps = float(scale_(layer->object()->config().slice_closing_radius.value)); + // merge with offset + ExPolygons merged = layer->merged(eps); + // ofsset back + ExPolygons layer_outline = offset_ex(merged, -eps); + + float min_region_flow_width { }; + for (const auto *region : layer->regions()) { + min_region_flow_width = std::max(min_region_flow_width, region->flow(FlowRole::frExternalPerimeter).width()); + } + std::cout << "min_region_flow_width: " << min_region_flow_width << std::endl; + return EdgeGridWrapper(scale_(min_region_flow_width), layer_outline); +} + +void check_extrusion_entity_stability(const ExtrusionEntity *entity, const EdgeGridWrapper &supported_grid, + const Params ¶ms) { + if (entity->is_collection()){ + for (const auto* e: static_cast(entity).entities){ + check_extrusion_entity_stability(e, supported_grid, params); + } + } else { //single extrusion path, with possible varying parameters + entity->as_polyline().points; + } + + +} + +void check_layer_stability(const PrintObject *po, size_t layer_idx, const Params ¶ms) { + if (layer_idx == 0) { + // first layer is usually ok + return; + } + const Layer *layer = po->get_layer(layer_idx); + const Layer *prev_layer = layer->lower_layer; + EdgeGridWrapper supported_grid = compute_layer_merged_edge_grid(prev_layer); + + for (const LayerRegion *layer_region : layer->regions()) { + coordf_t flow_width = coordf_t( + scale_(layer_region->flow(FlowRole::frExternalPerimeter).width())); + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { + if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { + check_extrusion_entity_stability(perimeter, supported_grid, params); + } // ex_perimeter + } // perimeter + } // ex_entity + } +} + +} //Impl End + +void quick_search(const PrintObject *po, const Params ¶ms = Params { }) { + using namespace Impl; + std::vector descriptors(po->layer_count()); + + tbb::parallel_for(tbb::blocked_range(0, po->layer_count()), + [&](tbb::blocked_range r) { + for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { + const Layer *layer = po->get_layer(layer_idx); + + LayerDescriptor &descriptor = descriptors[layer_idx]; + size_t point_count { 0 }; + + for (const LayerRegion *layer_region : layer->regions()) { + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { + if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { + assert(perimeter->is_loop()); + descriptor.segments_count++; + const ExtrusionLoop *loop = static_cast(perimeter); + for (const ExtrusionPath& path : loop->paths) { + Vec2f prev_pos = unscale(path.polyline.last_point()).cast(); + for (size_t p_idx = 0; p_idx < path.polyline.points.size(); ++p_idx) { + point_count++; + Vec2f point_pos = unscale(path.polyline.points[p_idx]).cast(); + descriptor.centroid += point_pos; + descriptor.perimeter_length += (point_pos - prev_pos).norm(); + prev_pos = point_pos; + } //point + } //path + } // ex_perimeter + } // perimeter +} // ex_entity +} // region + + descriptor.centroid /= float(point_count); + + } // layer + } // thread + ); + + for (size_t desc_idx = 0; desc_idx < descriptors.size(); ++desc_idx) { + const LayerDescriptor &descriptor = descriptors[desc_idx]; + std::cout << "SIS layer idx: " << desc_idx << " reg count: " << descriptor.segments_count << " len: " + << descriptor.perimeter_length << + " centroid: " << descriptor.centroid.x() << " | " << descriptor.centroid.y() << std::endl; + if (desc_idx > 0) { + const LayerDescriptor &prev = descriptors[desc_idx - 1]; + std::cout << "SIS diff: " << desc_idx << " reg count: " + << (int(descriptor.segments_count) - int(prev.segments_count)) << + " len: " << (descriptor.perimeter_length - prev.perimeter_length) << + " centroid: " << (descriptor.centroid - prev.centroid).norm() << std::endl; + } + } + +} + +} +} diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp new file mode 100644 index 0000000000..dbec52aff0 --- /dev/null +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -0,0 +1,16 @@ +#ifndef SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ +#define SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ + +#include "libslic3r/Print.hpp" + +namespace Slic3r { + +namespace SupportableIssues { + +void quick_search(const PrintObject *po); + +} + +} + +#endif /* SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ */