diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index b153578..c40401b 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -231,14 +231,16 @@ std::vector group_fills(const Layer &layer) fill.expolygons.emplace_back(std::move(fill.surface.expolygon)); //w21 fill.region_id_group.push_back(region_id); - //fill.no_overlap_expolygons = layerm.fill_no_overlap_expolygons; + //w21 + fill.no_overlap_expolygons = layerm.fill_no_overlap_expolygons; } else { //w21 fill.expolygons.emplace_back(surface.expolygon); auto t = find(fill.region_id_group.begin(), fill.region_id_group.end(), region_id); if (t == fill.region_id_group.end()) { fill.region_id_group.push_back(region_id); - //fill.no_overlap_expolygons = union_ex(fill.no_overlap_expolygons, layerm.fill_no_overlap_expolygons); + //w21 + fill.no_overlap_expolygons = union_ex(fill.no_overlap_expolygons, layerm.fill_no_overlap_expolygons); } } } diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 9c9d2f7..2c87480 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -621,6 +621,8 @@ void Layer::make_perimeters() std::vector> perimeter_and_gapfill_ranges; ExPolygons fill_expolygons; std::vector fill_expolygons_ranges; + //w21 + ExPolygons fill_no_overlap_expolygons; SurfacesPtr surfaces_to_merge; SurfacesPtr surfaces_to_merge_temp; @@ -690,7 +692,8 @@ void Layer::make_perimeters() } if (layer_region_ids.size() == 1) { // optimization - (*layerm)->make_perimeters((*layerm)->slices(), perimeter_and_gapfill_ranges, fill_expolygons, fill_expolygons_ranges); + //w21 + (*layerm)->make_perimeters((*layerm)->slices(), perimeter_and_gapfill_ranges, fill_expolygons, fill_expolygons_ranges,(*layerm)->fill_no_overlap_expolygons); this->sort_perimeters_into_islands((*layerm)->slices(), region_id, perimeter_and_gapfill_ranges, std::move(fill_expolygons), fill_expolygons_ranges, layer_region_ids); } else { SurfaceCollection new_slices; @@ -725,8 +728,24 @@ void Layer::make_perimeters() } } // make perimeters - layerm_config->make_perimeters(new_slices, perimeter_and_gapfill_ranges, fill_expolygons, fill_expolygons_ranges); + //w21 + ExPolygons fill_no_overlap; + layerm_config->make_perimeters(new_slices, perimeter_and_gapfill_ranges, fill_expolygons, fill_expolygons_ranges,fill_no_overlap); this->sort_perimeters_into_islands(new_slices, region_id_config, perimeter_and_gapfill_ranges, std::move(fill_expolygons), fill_expolygons_ranges, layer_region_ids); + //w21 + if (!new_slices.surfaces.empty()) { + for (size_t idx : layer_region_ids) { + // Separate the fill surfaces. + LayerRegion &layer = *m_regions[idx]; + ExPolygons expp = intersection_ex(new_slices.surfaces, layer.slices().surfaces); + layer.m_fill_expolygons = std::move(expp); + if (!layer.m_fill_expolygons.empty()) { + layer.m_fill_surfaces.set(std::move(layer.m_fill_expolygons), layer.slices().surfaces.front()); + layer.fill_no_overlap_expolygons = intersection_ex(layer.slices().surfaces, fill_no_overlap); + } + + } + } } } } diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 53ac003..4011cda 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -73,7 +73,9 @@ void LayerRegion::make_perimeters( // All fill areas produced for all input slices above. ExPolygons &fill_expolygons, // Ranges of fill areas above per input slice. - std::vector &fill_expolygons_ranges) + std::vector &fill_expolygons_ranges, + //w21 + ExPolygons &fill_no_overlap_expolygons) { m_perimeters.clear(); m_thin_fills.clear(); @@ -106,8 +108,12 @@ void LayerRegion::make_perimeters( // Cummulative sum of polygons over all the regions. const ExPolygons *lower_slices = this->layer()->lower_layer ? &this->layer()->lower_layer->lslices : nullptr; + //w16 + const ExPolygons *upper_slices = this->layer()->upper_layer ? &this->layer()->upper_layer->lslices : nullptr; // Cache for offsetted lower_slices Polygons lower_layer_polygons_cache; + //w16 + Polygons upper_layer_polygons_cache; for (const Surface &surface : slices) { auto perimeters_begin = uint32_t(m_perimeters.size()); @@ -115,16 +121,38 @@ void LayerRegion::make_perimeters( auto fill_expolygons_begin = uint32_t(fill_expolygons.size()); if (this->layer()->object()->config().perimeter_generator.value == PerimeterGeneratorType::Arachne && !spiral_vase) + //w16 + if (this->layer()->object()->config().top_one_wall_type == TopOneWallType::Alltop) + PerimeterGenerator::process_with_one_wall_arachne( + // input: + params, + surface, + lower_slices, + //w16 + upper_slices, + lower_layer_polygons_cache, + upper_layer_polygons_cache, + // output: + m_perimeters, + m_thin_fills, + fill_expolygons, + //w21 + fill_no_overlap_expolygons); + else PerimeterGenerator::process_arachne( // input: params, surface, lower_slices, + //w16 + upper_slices, lower_layer_polygons_cache, // output: m_perimeters, m_thin_fills, - fill_expolygons); + fill_expolygons, + //w21 + fill_no_overlap_expolygons); else PerimeterGenerator::process_classic( @@ -132,11 +160,16 @@ void LayerRegion::make_perimeters( params, surface, lower_slices, + //w16 + upper_slices, lower_layer_polygons_cache, + upper_layer_polygons_cache, // output: m_perimeters, m_thin_fills, - fill_expolygons); + fill_expolygons, + //w21 + fill_no_overlap_expolygons); perimeter_and_gapfill_ranges.emplace_back( ExtrusionRange{ perimeters_begin, uint32_t(m_perimeters.size()) }, ExtrusionRange{ gap_fills_begin, uint32_t(m_thin_fills.size()) }); diff --git a/src/libslic3r/LayerRegion.hpp b/src/libslic3r/LayerRegion.hpp index fa8bb8d..a03c5b2 100644 --- a/src/libslic3r/LayerRegion.hpp +++ b/src/libslic3r/LayerRegion.hpp @@ -92,6 +92,8 @@ public: // ordered collection of extrusion paths to fill surfaces // (this collection contains only ExtrusionEntityCollection objects) [[nodiscard]] const ExtrusionEntityCollection& fills() const { return m_fills; } + //w21 + ExPolygons fill_no_overlap_expolygons; Flow flow(FlowRole role) const; Flow flow(FlowRole role, double layer_height) const; @@ -109,7 +111,9 @@ public: // All fill areas produced for all input slices above. ExPolygons &fill_expolygons, // Ranges of fill areas above per input slice. - std::vector &fill_expolygons_ranges); + std::vector &fill_expolygons_ranges, + //w21 + ExPolygons &fill_no_overlap_expolygons); void process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered); double infill_area_threshold() const; // Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer. diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index ae98b1e..72ae0b2 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -1070,6 +1070,40 @@ std::tuple, Polygons> generate_extra_perimeters_over } +//w16 +void PerimeterGenerator::add_infill_contour_for_arachne(ExPolygons infill_contour, + int loops, + coord_t ext_perimeter_spacing, + coord_t perimeter_spacing, + coord_t min_perimeter_infill_spacing, + coord_t spacing, + bool is_inner_part, + const Parameters ¶ms, + ExPolygons &infill_areas, + ExPolygons & out_fill_expolygons, + //w21 + ExPolygons & out_fill_no_overlap) +{ + if (offset_ex(infill_contour, -float(spacing / 2.)).empty()) { + infill_contour.clear(); + } + coord_t insert = (loops < 0) ? 0 : ext_perimeter_spacing; + if (is_inner_part || loops > 0) + insert = perimeter_spacing; + + insert = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale(insert)))); + Polygons inner_pp; + for (ExPolygon &ex : infill_contour) + ex.simplify_p(params.scaled_resolution, &inner_pp); + ExPolygons inner_union = union_ex(inner_pp); + float offset1 = -min_perimeter_infill_spacing / 2.; + float offset2 = insert + min_perimeter_infill_spacing / 2.; + infill_areas = offset2_ex(inner_union, offset1, offset2); + append(out_fill_expolygons, offset2_ex(union_ex(inner_pp), float(-min_perimeter_infill_spacing / 2.), float(insert + min_perimeter_infill_spacing / 2.))); + //w21 + append(out_fill_no_overlap,offset2_ex(inner_union, float(-min_perimeter_infill_spacing / 2.), float(min_perimeter_infill_spacing / 2.))); +} + // Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper // "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling" @@ -1078,6 +1112,7 @@ void PerimeterGenerator::process_arachne( const Parameters ¶ms, const Surface &surface, const ExPolygons *lower_slices, + const ExPolygons *upper_slices, // Cache: Polygons &lower_slices_polygons_cache, // Output: @@ -1086,7 +1121,9 @@ void PerimeterGenerator::process_arachne( // Gaps without the thin walls ExtrusionEntityCollection & /* out_gap_fill */, // Infills without the gap fills - ExPolygons &out_fill_expolygons) + ExPolygons &out_fill_expolygons, + //w21 + ExPolygons &out_fill_no_overlap) { // other perimeters coord_t perimeter_spacing = params.perimeter_flow.scaled_spacing(); @@ -1113,6 +1150,9 @@ void PerimeterGenerator::process_arachne( ExPolygons last = offset_ex(surface.expolygon.simplify_p(params.scaled_resolution), - float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.)); Polygons last_p = to_polygons(last); + //w16 + if (upper_slices == nullptr && params.object_config.top_one_wall_type == TopOneWallType::Onlytopmost) + loop_number = 0; Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, params.layer_height, params.object_config, params.print_config); std::vector perimeters = wallToolPaths.getToolPaths(); @@ -1269,10 +1309,21 @@ void PerimeterGenerator::process_arachne( out_loops.append(extrusion_coll); ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour()); + //w17 + ExPolygons the_layer_surface = infill_contour; const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing; if (offset_ex(infill_contour, -float(spacing / 2.)).empty()) infill_contour.clear(); // Infill region is too small, so let's filter it out. + if (params.object_config.top_one_wall_type != TopOneWallType::Disable) { + coord_t perimeter_width = params.perimeter_flow.scaled_width(); + double min_width_top_surface = (params.object_config.top_area_threshold / 100) * + std::max(double(ext_perimeter_spacing / 4 + 10), double(perimeter_width / 4)); + infill_contour = offset2_ex(infill_contour, -min_width_top_surface, min_width_top_surface + perimeter_width); + ExPolygons surface_not_export_to_top = diff_ex(the_layer_surface, infill_contour); + } + // BBS: get real top surface + infill_contour = intersection_ex(infill_contour, the_layer_surface); // create one more offset to be used as boundary for fill // we offset by half the perimeter spacing (to get to the actual infill boundary) // and then we offset back and forth by half the infill spacing to only consider the @@ -1316,24 +1367,411 @@ void PerimeterGenerator::process_arachne( infill_areas = diff_ex(infill_areas, filled_area); } } + //w21 + append(out_fill_no_overlap, offset2_ex(union_ex(pp),float(-min_perimeter_infill_spacing / 2.), float( min_perimeter_infill_spacing / 2.))); + append(out_fill_expolygons, std::move(infill_areas)); +} +void PerimeterGenerator::process_with_one_wall_arachne( + // Inputs: + const Parameters ¶ms, + const Surface &surface, + const ExPolygons *lower_slices, + //w16 + const ExPolygons *upper_slices, + // Cache: + Polygons &lower_slices_polygons_cache, + Polygons &upper_slices_polygons_cache, + // Output: + // Loops with the external thin walls + ExtrusionEntityCollection &out_loops, + // Gaps without the thin walls + ExtrusionEntityCollection & /* out_gap_fill */, + // Infills without the gap fills + ExPolygons &out_fill_expolygons, + //w21 + ExPolygons &out_fill_no_overlap) +{ + // other perimeters + coord_t perimeter_spacing = params.perimeter_flow.scaled_spacing(); + // external perimeters + coord_t ext_perimeter_width = params.ext_perimeter_flow.scaled_width(); + coord_t ext_perimeter_spacing = params.ext_perimeter_flow.scaled_spacing(); + coord_t ext_perimeter_spacing2 = scaled(0.5f * (params.ext_perimeter_flow.spacing() + params.perimeter_flow.spacing())); + // solid infill + coord_t solid_infill_spacing = params.solid_infill_flow.scaled_spacing(); + + // prepare grown lower layer slices for overhang detection + if (params.config.overhangs && lower_slices != nullptr && lower_slices_polygons_cache.empty()) { + // We consider overhang any part where the entire nozzle diameter is not supported by the + // lower layer, so we take lower slices and offset them by half the nozzle diameter used + // in the current layer + double nozzle_diameter = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder-1); + lower_slices_polygons_cache = offset(*lower_slices, float(scale_(+nozzle_diameter/2))); + } + if (params.config.overhangs && upper_slices != nullptr && upper_slices_polygons_cache.empty()) { + double upper_nozzle_diameter = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder - 1); + upper_slices_polygons_cache = offset(*upper_slices, float(scale_(EPSILON))); + } + + + // we need to process each island separately because we might have different + // extra perimeters for each one + // detect how many perimeters must be generated for this island + int loop_number = params.config.perimeters + surface.extra_perimeters - 1; // 0-indexed loops + ExPolygons last = offset_ex(surface.expolygon.simplify_p(params.scaled_resolution), - float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.)); + Polygons last_p = to_polygons(last); + + int remain_loops = -1; + if (params.object_config.top_one_wall_type == TopOneWallType::Alltop) { + if (upper_slices != nullptr) + remain_loops = loop_number - 1; + + loop_number = 0; + } + + Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, params.layer_height, params.object_config, params.print_config); + std::vector perimeters = wallToolPaths.getToolPaths(); + loop_number = int(perimeters.size()) - 1; + + + + //w16 + ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour()); + ExPolygons inner_infill_contour; + + if (remain_loops >= 0) { + + ExPolygons the_layer_surface = infill_contour; + BoundingBox infill_contour_box = get_extents(infill_contour); + infill_contour_box.offset(SCALED_EPSILON); + Polygons upper_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(upper_slices_polygons_cache, + infill_contour_box); + + infill_contour = diff_ex(infill_contour, upper_polygons_series_clipped); + + coord_t perimeter_width = params.perimeter_flow.scaled_width(); + if (lower_slices != nullptr) { + BoundingBox infill_contour_box = get_extents(infill_contour); + infill_contour_box.offset(SCALED_EPSILON); + Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_slices_polygons_cache, + infill_contour_box); + + ExPolygons bridge_area = offset_ex(diff_ex(infill_contour, lower_polygons_series_clipped), + std::max(ext_perimeter_spacing, perimeter_width)); + infill_contour = diff_ex(infill_contour, bridge_area); + } + //w17 + // double min_width_top_surface = std::max(double(ext_perimeter_spacing / 4 + 10), double(perimeter_width / 4)); + double min_width_top_surface = (params.object_config.top_area_threshold / 100) * std::max(double(ext_perimeter_spacing / 4 + 10), double(perimeter_width / 4)); + infill_contour = offset2_ex(infill_contour, -min_width_top_surface, min_width_top_surface + perimeter_width); + + ExPolygons surface_not_export_to_top = diff_ex(the_layer_surface, infill_contour); + + infill_contour = intersection_ex(infill_contour, the_layer_surface); + Polygons surface_not_export_to_top_p = to_polygons(surface_not_export_to_top); + Arachne::WallToolPaths innerWallToolPaths(surface_not_export_to_top_p, perimeter_spacing, perimeter_spacing, + coord_t(remain_loops + 1), 0, params.layer_height, params.object_config, params.print_config); + + std::vector perimeters_inner = innerWallToolPaths.getToolPaths(); + remain_loops = int(perimeters_inner.size()) - 1; + if (!perimeters.empty()) { + for (int perimeter_idx = 0; perimeter_idx < perimeters_inner.size(); perimeter_idx++) { + if (perimeters_inner[perimeter_idx].empty()) + continue; + + for (Arachne::ExtrusionLine &wall : perimeters_inner[perimeter_idx]) { + wall.inset_idx++; + } + } + } + perimeters.insert(perimeters.end(), perimeters_inner.begin(), perimeters_inner.end()); + + inner_infill_contour = union_ex(innerWallToolPaths.getInnerContour()); + } + +#ifdef ARACHNE_DEBUG + { + static int iRun = 0; + export_perimeters_to_svg(debug_out_path("arachne-perimeters-%d-%d.svg", layer_id, iRun++), to_polygons(last), perimeters, union_ex(wallToolPaths.getInnerContour())); + } +#endif + + // All closed ExtrusionLine should have the same the first and the last point. + // But in rare cases, Arachne produce ExtrusionLine marked as closed but without + // equal the first and the last point. + assert([&perimeters = std::as_const(perimeters)]() -> bool { + for (const Arachne::VariableWidthLines &perimeter : perimeters) + for (const Arachne::ExtrusionLine &el : perimeter) + if (el.is_closed && el.junctions.front().p != el.junctions.back().p) + return false; + return true; + }()); + + int start_perimeter = int(perimeters.size()) - 1; + int end_perimeter = -1; + int direction = -1; + + if (params.config.external_perimeters_first) { + start_perimeter = 0; + end_perimeter = int(perimeters.size()); + direction = 1; + } + + std::vector all_extrusions; + for (int perimeter_idx = start_perimeter; perimeter_idx != end_perimeter; perimeter_idx += direction) { + if (perimeters[perimeter_idx].empty()) + continue; + for (Arachne::ExtrusionLine &wall : perimeters[perimeter_idx]) + all_extrusions.emplace_back(&wall); + } + + // Find topological order with constraints from extrusions_constrains. + std::vector blocked(all_extrusions.size(), 0); // Value indicating how many extrusions it is blocking (preceding extrusions) an extrusion. + std::vector> blocking(all_extrusions.size()); // Each extrusion contains a vector of extrusions that are blocked by this extrusion. + ankerl::unordered_dense::map map_extrusion_to_idx; + for (size_t idx = 0; idx < all_extrusions.size(); idx++) + map_extrusion_to_idx.emplace(all_extrusions[idx], idx); + + Arachne::WallToolPaths::ExtrusionLineSet extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, params.config.external_perimeters_first); + for (auto [before, after] : extrusions_constrains) { + auto after_it = map_extrusion_to_idx.find(after); + ++blocked[after_it->second]; + blocking[map_extrusion_to_idx.find(before)->second].emplace_back(after_it->second); + } + + std::vector processed(all_extrusions.size(), false); // Indicate that the extrusion was already processed. + Point current_position = all_extrusions.empty() ? Point::Zero() : all_extrusions.front()->junctions.front().p; // Some starting position. + std::vector ordered_extrusions; // To store our result in. At the end we'll std::swap. + ordered_extrusions.reserve(all_extrusions.size()); + + while (ordered_extrusions.size() < all_extrusions.size()) { + size_t best_candidate = 0; + double best_distance_sqr = std::numeric_limits::max(); + bool is_best_closed = false; + + std::vector available_candidates; + for (size_t candidate = 0; candidate < all_extrusions.size(); ++candidate) { + if (processed[candidate] || blocked[candidate]) + continue; // Not a valid candidate. + available_candidates.push_back(candidate); + } + + std::sort(available_candidates.begin(), available_candidates.end(), [&all_extrusions](const size_t a_idx, const size_t b_idx) -> bool { + return all_extrusions[a_idx]->is_closed < all_extrusions[b_idx]->is_closed; + }); + + for (const size_t candidate_path_idx : available_candidates) { + auto& path = all_extrusions[candidate_path_idx]; + + if (path->junctions.empty()) { // No vertices in the path. Can't find the start position then or really plan it in. Put that at the end. + if (best_distance_sqr == std::numeric_limits::max()) { + best_candidate = candidate_path_idx; + is_best_closed = path->is_closed; + } + continue; + } + + const Point candidate_position = path->junctions.front().p; + double distance_sqr = (current_position - candidate_position).cast().norm(); + if (distance_sqr < best_distance_sqr) { // Closer than the best candidate so far. + if (path->is_closed || (!path->is_closed && best_distance_sqr != std::numeric_limits::max()) || (!path->is_closed && !is_best_closed)) { + best_candidate = candidate_path_idx; + best_distance_sqr = distance_sqr; + is_best_closed = path->is_closed; + } + } + } + + auto &best_path = all_extrusions[best_candidate]; + ordered_extrusions.push_back({best_path, best_path->is_contour(), false}); + processed[best_candidate] = true; + for (size_t unlocked_idx : blocking[best_candidate]) + blocked[unlocked_idx]--; + + if (!best_path->junctions.empty()) { //If all paths were empty, the best path is still empty. We don't upate the current position then. + if(best_path->is_closed) + current_position = best_path->junctions[0].p; //We end where we started. + else + current_position = best_path->junctions.back().p; //Pick the other end from where we started. + } + } + + if (params.layer_id > 0 && params.config.fuzzy_skin != FuzzySkinType::None) { + std::vector closed_loop_extrusions; + for (PerimeterGeneratorArachneExtrusion &extrusion : ordered_extrusions) + if (extrusion.extrusion->inset_idx == 0) { + if (extrusion.extrusion->is_closed && params.config.fuzzy_skin == FuzzySkinType::External) { + closed_loop_extrusions.emplace_back(&extrusion); + } else { + extrusion.fuzzify = true; + } + } + + if (params.config.fuzzy_skin == FuzzySkinType::External) { + ClipperLib_Z::Paths loops_paths; + loops_paths.reserve(closed_loop_extrusions.size()); + for (const auto &cl_extrusion : closed_loop_extrusions) { + assert(cl_extrusion->extrusion->junctions.front() == cl_extrusion->extrusion->junctions.back()); + size_t loop_idx = &cl_extrusion - &closed_loop_extrusions.front(); + ClipperLib_Z::Path loop_path; + loop_path.reserve(cl_extrusion->extrusion->junctions.size() - 1); + for (auto junction_it = cl_extrusion->extrusion->junctions.begin(); junction_it != std::prev(cl_extrusion->extrusion->junctions.end()); ++junction_it) + loop_path.emplace_back(junction_it->p.x(), junction_it->p.y(), loop_idx); + loops_paths.emplace_back(loop_path); + } + + ClipperLib_Z::Clipper clipper; + clipper.AddPaths(loops_paths, ClipperLib_Z::ptSubject, true); + ClipperLib_Z::PolyTree loops_polytree; + clipper.Execute(ClipperLib_Z::ctUnion, loops_polytree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd); + + for (const ClipperLib_Z::PolyNode *child_node : loops_polytree.Childs) { + // The whole contour must have the same index. + coord_t polygon_idx = child_node->Contour.front().z(); + bool has_same_idx = std::all_of(child_node->Contour.begin(), child_node->Contour.end(), + [&polygon_idx](const ClipperLib_Z::IntPoint &point) -> bool { return polygon_idx == point.z(); }); + if (has_same_idx) + closed_loop_extrusions[polygon_idx]->fuzzify = true; + } + } + } + + if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(params, lower_slices_polygons_cache, ordered_extrusions); !extrusion_coll.empty()) + out_loops.append(extrusion_coll); + + + //w16 + if (remain_loops >= 0) { + const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing; + if (offset_ex(infill_contour, -float(spacing / 2.)).empty()) + infill_contour.clear(); + coord_t inset = (loop_number < 0) ? 0 : + (loop_number == 0) ? + // one loop + ext_perimeter_spacing : + // two or more loops? + perimeter_spacing; + + inset = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale(inset)))); + Polygons pp; + for (ExPolygon &ex : infill_contour) + ex.simplify_p(params.scaled_resolution, &pp); + // collapse too narrow infill areas + const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE)); + // append infill areas to fill_surfaces + ExPolygons infill_areas = offset2_ex(union_ex(pp), float(-min_perimeter_infill_spacing / 2.), + float(inset + min_perimeter_infill_spacing / 2.)); + // ExPolygons infill_areas; + + if (lower_slices != nullptr && params.config.overhangs && params.config.extra_perimeters_on_overhangs && + params.config.perimeters > 0 && params.layer_id > params.object_config.raft_layers) { + // Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material + auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas, lower_slices_polygons_cache, + loop_number + 1, params.overhang_flow, + params.scaled_resolution, params.object_config, + params.print_config); + if (!extra_perimeters.empty()) { + ExtrusionEntityCollection &this_islands_perimeters = static_cast(*out_loops.entities.back()); + ExtrusionEntitiesPtr old_entities; + old_entities.swap(this_islands_perimeters.entities); + for (ExtrusionPaths &paths : extra_perimeters) + this_islands_perimeters.append(std::move(paths)); + append(this_islands_perimeters.entities, old_entities); + infill_areas = diff_ex(infill_areas, filled_area); + } + } + + inset = (loop_number < 0) ? 0 : + (loop_number == 0) ? + // one loop + ext_perimeter_spacing : + // two or more loops? + perimeter_spacing; + + inset = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale(inset)))); + for (ExPolygon &ex : infill_contour) + ex.simplify_p(params.scaled_resolution, &pp); + // collapse too narrow infill areas + + + if (remain_loops >= 0) { + //w21 + add_infill_contour_for_arachne(infill_contour, loop_number, ext_perimeter_spacing, perimeter_spacing, + min_perimeter_infill_spacing, spacing, true, params, infill_areas, out_fill_expolygons,out_fill_no_overlap); + } + + + if (remain_loops >= 0) { + if (!inner_infill_contour.empty()) + //w21 + add_infill_contour_for_arachne(inner_infill_contour, remain_loops, ext_perimeter_spacing, perimeter_spacing, + min_perimeter_infill_spacing, spacing, true, params, infill_areas, out_fill_expolygons,out_fill_no_overlap); + } + //w21 + append(out_fill_no_overlap, + offset2_ex(union_ex(pp), float(-min_perimeter_infill_spacing / 2.), float(min_perimeter_infill_spacing / 2.))); + append(out_fill_expolygons, std::move(infill_areas)); + } else { + infill_contour = union_ex(wallToolPaths.getInnerContour()); + const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing; + if (offset_ex(infill_contour, -float(spacing / 2.)).empty()) + infill_contour.clear(); // Infill region is too small, so let's filter it out. + + coord_t inset = (loop_number < 0) ? 0 : (loop_number == 0) ? ext_perimeter_spacing : perimeter_spacing; + + inset = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale(inset)))); + Polygons pp; + for (ExPolygon &ex : infill_contour) + ex.simplify_p(params.scaled_resolution, &pp); + // collapse too narrow infill areas + const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE)); + // append infill areas to fill_surfaces + ExPolygons infill_areas = offset2_ex(union_ex(pp), float(-min_perimeter_infill_spacing / 2.), + float(inset + min_perimeter_infill_spacing / 2.)); + + if (lower_slices != nullptr && params.config.overhangs && params.config.extra_perimeters_on_overhangs && + params.config.perimeters > 0 && params.layer_id > params.object_config.raft_layers) { + // Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material + auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas, lower_slices_polygons_cache, + loop_number + 1, params.overhang_flow, + params.scaled_resolution, params.object_config, + params.print_config); + if (!extra_perimeters.empty()) { + ExtrusionEntityCollection &this_islands_perimeters = static_cast(*out_loops.entities.back()); + ExtrusionEntitiesPtr old_entities; + old_entities.swap(this_islands_perimeters.entities); + for (ExtrusionPaths &paths : extra_perimeters) + this_islands_perimeters.append(std::move(paths)); + append(this_islands_perimeters.entities, old_entities); + infill_areas = diff_ex(infill_areas, filled_area); + } + } + //w21 + append(out_fill_no_overlap,offset2_ex(union_ex(pp), float(-min_perimeter_infill_spacing / 2.), float(min_perimeter_infill_spacing / 2.))); append(out_fill_expolygons, std::move(infill_areas)); } +} void PerimeterGenerator::process_classic( // Inputs: const Parameters ¶ms, const Surface &surface, const ExPolygons *lower_slices, + //w16 + const ExPolygons *upper_slices, // Cache: - Polygons &lower_slices_polygons_cache, + Polygons &lower_layer_polygons_cache, + Polygons &upper_layer_polygons_cache, // Output: // Loops with the external thin walls ExtrusionEntityCollection &out_loops, // Gaps without the thin walls ExtrusionEntityCollection &out_gap_fill, // Infills without the gap fills - ExPolygons &out_fill_expolygons) + ExPolygons &out_fill_expolygons, + //w21 + ExPolygons &out_fill_no_overlap) { // other perimeters coord_t perimeter_width = params.perimeter_flow.scaled_width(); @@ -1360,12 +1798,12 @@ void PerimeterGenerator::process_classic( bool has_gap_fill = params.config.gap_fill_enabled.value && params.config.gap_fill_speed.value > 0; // prepare grown lower layer slices for overhang detection - if (params.config.overhangs && lower_slices != nullptr && lower_slices_polygons_cache.empty()) { + if (params.config.overhangs && lower_slices != nullptr && lower_layer_polygons_cache.empty()) { // We consider overhang any part where the entire nozzle diameter is not supported by the // lower layer, so we take lower slices and offset them by half the nozzle diameter used // in the current layer double nozzle_diameter = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder-1); - lower_slices_polygons_cache = offset(*lower_slices, float(scale_(+nozzle_diameter/2))); + lower_layer_polygons_cache = offset(*lower_slices, float(scale_(+nozzle_diameter / 2))); } // we need to process each island separately because we might have different @@ -1374,6 +1812,17 @@ void PerimeterGenerator::process_classic( int loop_number = params.config.perimeters + surface.extra_perimeters - 1; // 0-indexed loops ExPolygons last = union_ex(surface.expolygon.simplify_p(params.scaled_resolution)); ExPolygons gaps; + //w16 + ExPolygons fill_clip; + ExPolygons top_fills; + //w16 + + if (params.config.overhangs && upper_slices != nullptr && upper_layer_polygons_cache.empty()) { + double upper_nozzle_diameter = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder - 1); + upper_layer_polygons_cache = offset(*upper_slices, float(scale_(+upper_nozzle_diameter / 2))); + } + if (loop_number > 0 && params.object_config.top_one_wall_type != TopOneWallType::Disable && upper_slices == nullptr) + loop_number = 0; if (loop_number >= 0) { // In case no perimeters are to be generated, loop_number will equal to -1. std::vector contours(loop_number+1); // depth => loops @@ -1413,20 +1862,22 @@ void PerimeterGenerator::process_classic( //FIXME Is this offset correct if the line width of the inner perimeters differs // from the line width of the infill? coord_t distance = (i == 1) ? ext_perimeter_spacing2 : perimeter_spacing; - offsets = params.config.thin_walls ? + //w16 + //offsets = params.config.thin_walls ? // This path will ensure, that the perimeters do not overfill, as in - // prusa3d/Slic3r GH #32, but with the cost of rounding the perimeters + // qidi3d/Slic3r GH #32, but with the cost of rounding the perimeters // excessively, creating gaps, which then need to be filled in by the not very // reliable gap fill algorithm. // Also the offset2(perimeter, -x, x) may sometimes lead to a perimeter, which is larger than // the original. - offset2_ex(last, - - float(distance + min_spacing / 2. - 1.), - float(min_spacing / 2. - 1.)) : + //offset2_ex(last, + // - float(distance + min_spacing / 2. - 1.), + // float(min_spacing / 2. - 1.)) : // If "detect thin walls" is not enabled, this paths will be entered, which - // leads to overflows, as in prusa3d/Slic3r GH #32 - offset_ex(last, - float(distance)); + // leads to overflows, as in qidi3d/Slic3r GH #32 + // offset_ex(last, - float(distance)); // look for gaps + offsets = offset2_ex(last, -float(distance + min_spacing / 2. - 1.), float(min_spacing / 2. - 1.)); if (has_gap_fill) // not using safety offset here would "detect" very narrow gaps // (but still long enough to escape the area threshold) that gap fill @@ -1465,6 +1916,51 @@ void PerimeterGenerator::process_classic( } last = std::move(offsets); + //w16 + if (i == 0 && i != loop_number && params.object_config.top_one_wall_type == TopOneWallType::Alltop && + upper_slices != nullptr) { + coord_t offset_top_surface = scale_( + 1.5 * (params.config.perimeters.value == 0 ? + 0. : + unscaled(double(ext_perimeter_width + perimeter_spacing * int(int(params.config.perimeters.value) - int(1)))))); + if (offset_top_surface > 0.9 * (params.config.perimeters.value <= 1 ? 0. : (perimeter_spacing * (params.config.perimeters.value - 1)))) + offset_top_surface -= coord_t( + 0.9 * (params.config.perimeters.value <= 1 ? 0. : (perimeter_spacing * (params.config.perimeters.value - 1)))); + else + offset_top_surface = 0; + //w17 + double min_width_top_surface = (params.object_config.top_area_threshold/100 ) * + std::max(double(ext_perimeter_spacing / 2 + 10), 1.0 * (double(perimeter_width))); + BoundingBox last_box = get_extents(last); + last_box.offset(SCALED_EPSILON); + Polygons upper_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(upper_layer_polygons_cache, + last_box); + upper_polygons_series_clipped = offset(upper_polygons_series_clipped, min_width_top_surface); + fill_clip = offset_ex(last, -double(ext_perimeter_spacing)); + ExPolygons bridge_checker; + if (lower_slices != nullptr) { + Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_layer_polygons_cache, + last_box); + + double bridge_offset = std::max(double(ext_perimeter_spacing), (double(perimeter_width))); + bridge_checker = offset_ex(diff_ex(last, lower_polygons_series_clipped, ApplySafetyOffset::Yes), 1.5 * bridge_offset); + } + ExPolygons delete_bridge = diff_ex(last, bridge_checker, ApplySafetyOffset::Yes); + + ExPolygons top_polygons = diff_ex(delete_bridge, upper_polygons_series_clipped, ApplySafetyOffset::Yes); + ExPolygons temp_gap = diff_ex(top_polygons, fill_clip); + ExPolygons inner_polygons = diff_ex(last, + offset_ex(top_polygons, offset_top_surface + min_width_top_surface - + double(ext_perimeter_spacing / 2)), + ApplySafetyOffset::Yes); + top_polygons = diff_ex(fill_clip, inner_polygons, ApplySafetyOffset::Yes); + top_fills = union_ex(top_fills, top_polygons); + double infill_spacing_unscaled = params.config.infill_extrusion_width.value; // this->config->sparse_infill_line_width.value; + fill_clip = offset_ex(last, double(ext_perimeter_spacing / 2) - scale_(infill_spacing_unscaled / 2)); + last = intersection_ex(inner_polygons, last); + if (has_gap_fill) + last = union_ex(last, temp_gap); + } if (i == loop_number && (! has_gap_fill || params.config.fill_density.value == 0)) { @@ -1529,7 +2025,7 @@ void PerimeterGenerator::process_classic( } } // at this point, all loops should be in contours[0] - ExtrusionEntityCollection entities = traverse_loops_classic(params, lower_slices_polygons_cache, contours.front(), thin_walls); + ExtrusionEntityCollection entities = traverse_loops_classic(params, lower_layer_polygons_cache, contours.front(), thin_walls); // if brim will be printed, reverse the order of perimeters so that // we continue inwards after having finished the brim // TODO: add test for perimeter order @@ -1580,9 +2076,14 @@ void PerimeterGenerator::process_classic( ext_perimeter_spacing / 2 : // two or more loops? perimeter_spacing / 2; + //w21 + coord_t infill_peri_overlap = 0; // only apply infill overlap if we actually have one perimeter - if (inset > 0) - inset -= coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale(inset + solid_infill_spacing / 2)))); + //w21 + if (inset > 0) { + infill_peri_overlap = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale( solid_infill_spacing / 2)))); + inset -= infill_peri_overlap; + } // simplify infill contours according to resolution Polygons pp; for (ExPolygon &ex : last) @@ -1596,12 +2097,34 @@ void PerimeterGenerator::process_classic( float(- inset - min_perimeter_infill_spacing / 2.), float(min_perimeter_infill_spacing / 2.)); + ExPolygons top_infill_exp = intersection_ex(fill_clip, offset_ex(top_fills, double(ext_perimeter_spacing / 2))); + //w21 + if (!top_fills.empty()) { + infill_areas = union_ex(infill_areas, offset_ex(top_infill_exp, double(infill_peri_overlap))); + } + append(out_fill_expolygons, std::move(top_infill_exp)); + //w21 + { + ExPolygons polyWithoutOverlap; + if (min_perimeter_infill_spacing / 2 > infill_peri_overlap) + polyWithoutOverlap = offset2_ex( + union_ex(pp), + float(-inset - min_perimeter_infill_spacing / 2.), + float(min_perimeter_infill_spacing / 2 - infill_peri_overlap)); + else + polyWithoutOverlap = offset_ex( + union_ex(pp), + double(-inset - infill_peri_overlap)); + if (!top_fills.empty()) + polyWithoutOverlap = union_ex(polyWithoutOverlap, top_infill_exp); + out_fill_no_overlap.insert(out_fill_no_overlap.end(), polyWithoutOverlap.begin(), polyWithoutOverlap.end()); + } if (lower_slices != nullptr && params.config.overhangs && params.config.extra_perimeters_on_overhangs && params.config.perimeters > 0 && params.layer_id > params.object_config.raft_layers) { // Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas, - lower_slices_polygons_cache, + lower_layer_polygons_cache, loop_number + 1, params.overhang_flow, params.scaled_resolution, params.object_config, params.print_config); @@ -1619,4 +2142,5 @@ void PerimeterGenerator::process_classic( append(out_fill_expolygons, std::move(infill_areas)); } + } diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index 96ab5a1..038aabb 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -70,21 +70,28 @@ void process_classic( const Parameters ¶ms, const Surface &surface, const ExPolygons *lower_slices, + //w16 + const ExPolygons *upper_slices, // Cache: - Polygons &lower_slices_polygons_cache, + Polygons &lower_layer_polygons_cache, + Polygons &upper_layer_polygons_cache, // Output: // Loops with the external thin walls ExtrusionEntityCollection &out_loops, // Gaps without the thin walls ExtrusionEntityCollection &out_gap_fill, // Infills without the gap fills - ExPolygons &out_fill_expolygons); + ExPolygons &out_fill_expolygons, + //w21 + ExPolygons &out_fill_no_overlap); void process_arachne( // Inputs: const Parameters ¶ms, const Surface & surface, const ExPolygons *lower_slices, + //w16 + const ExPolygons *upper_slices, // Cache: Polygons &lower_slices_polygons_cache, // Output: @@ -93,7 +100,43 @@ void process_arachne( // Gaps without the thin walls ExtrusionEntityCollection &out_gap_fill, // Infills without the gap fills - ExPolygons &out_fill_expolygons); + ExPolygons &out_fill_expolygons, + //w21 + ExPolygons &out_fill_no_overlap); + +void process_with_one_wall_arachne( + // Inputs: + const Parameters ¶ms, + const Surface &surface, + const ExPolygons *lower_slices, + //w16 + const ExPolygons *upper_slices, + // Cache: + Polygons &lower_slices_polygons_cache, + Polygons &upper_slices_polygons_cache, + // Output: + // Loops with the external thin walls + ExtrusionEntityCollection &out_loops, + // Gaps without the thin walls + ExtrusionEntityCollection &out_gap_fill, + // Infills without the gap fills + ExPolygons &out_fill_expolygons, + //w21 + ExPolygons &out_fill_no_overlap); + +//w16 +void add_infill_contour_for_arachne(ExPolygons infill_contour, + int loops, + coord_t ext_perimeter_spacing, + coord_t perimeter_spacing, + coord_t min_perimeter_infill_spacing, + coord_t spacing, + bool is_inner_part, + const Parameters ¶ms, + ExPolygons & infill_areas, + ExPolygons & out_fill_expolygons, + //w21 + ExPolygons & out_fill_no_overlap); ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, float tolerance, float merge_tolerance);