From eba6ab9ac141cb69f91ef2156c7c4d1e8284f855 Mon Sep 17 00:00:00 2001 From: Wang YB <94800665+Gradbb@users.noreply.github.com> Date: Wed, 17 Jan 2024 10:59:37 +0800 Subject: [PATCH] add top one wall --- src/libslic3r/LayerRegion.cpp | 27 +- src/libslic3r/PerimeterGenerator.cpp | 479 +++++++++++++++++++++++++- src/libslic3r/PerimeterGenerator.hpp | 35 ++ src/libslic3r/Preset.cpp | 2 + src/libslic3r/PrintConfig.cpp | 19 + src/libslic3r/PrintConfig.hpp | 5 + src/libslic3r/PrintObject.cpp | 5 +- src/slic3r/GUI/ConfigManipulation.cpp | 1 - src/slic3r/GUI/Tab.cpp | 2 + 9 files changed, 566 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 73f225c..2274cfa 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -106,31 +106,56 @@ 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; + Polygons upper_layer_polygons_cache; for (const Surface &surface : slices) { auto perimeters_begin = uint32_t(m_perimeters.size()); auto gap_fills_begin = uint32_t(m_thin_fills.size()); auto fill_expolygons_begin = uint32_t(fill_expolygons.size()); if (this->layer()->object()->config().perimeter_generator.value == PerimeterGeneratorType::Arachne && !spiral_vase) - PerimeterGenerator::process_arachne( + + //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); + else + PerimeterGenerator::process_arachne( + // input: + params, + surface, + lower_slices, + upper_slices, lower_layer_polygons_cache, // output: m_perimeters, m_thin_fills, fill_expolygons); + else PerimeterGenerator::process_classic( // input: params, surface, lower_slices, + //w16 + upper_slices, lower_layer_polygons_cache, + upper_layer_polygons_cache, // output: m_perimeters, m_thin_fills, diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 1f81dcc..24d73e3 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -1069,6 +1069,37 @@ std::tuple, Polygons> generate_extra_perimeters_over return {extra_perims, diff(inset_overhang_area, inset_overhang_area_left_unfilled)}; } +//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) +{ + 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, std::move(infill_areas)); +} + + // 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" void PerimeterGenerator::process_arachne( @@ -1076,6 +1107,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: @@ -1111,6 +1143,10 @@ 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(); loop_number = int(perimeters.size()) - 1; @@ -1317,13 +1353,387 @@ void PerimeterGenerator::process_arachne( 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) +{ + // 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_(+upper_nozzle_diameter / 2))); + } + + + // 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); + } + double min_width_top_surface = 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) { + 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); + } + + + if (remain_loops >= 0) { + if (!inner_infill_contour.empty()) + 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); + } + 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); + } + } + + 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 &upper_layer_polygons_cache, // Output: // Loops with the external thin walls ExtrusionEntityCollection &out_loops, @@ -1371,6 +1781,11 @@ 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; + if (loop_number > 0 && params.object_config.top_one_wall_type != TopOneWallType::None && 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 @@ -1410,20 +1825,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 // 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 qidi3d/Slic3r GH #32 - offset_ex(last, - float(distance)); + // 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 @@ -1461,6 +1878,54 @@ 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; + double min_width_top_surface = (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 grown_lower_slices; + ExPolygons bridge_checker; + if (lower_slices != nullptr) { + Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_slices_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)) { // The last run of this loop is executed to collect gaps for gap fill. // As the gap fill is either disabled or not @@ -1590,6 +2055,10 @@ 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))); + + append(out_fill_expolygons, std::move(top_infill_exp)); + 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 diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index 4908e4f..1c78659 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -70,8 +70,11 @@ 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 &upper_slices_polygons_cache, // Output: // Loops with the external thin walls ExtrusionEntityCollection &out_loops, @@ -81,12 +84,32 @@ void process_classic( ExPolygons &out_fill_expolygons); 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: + // 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); + +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, @@ -95,6 +118,18 @@ void process_arachne( // Infills without the gap fills ExPolygons &out_fill_expolygons); +//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); + ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, float tolerance, float merge_tolerance); } // namespace PerimeterGenerator diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 65c8e33..c03e1a5 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -475,6 +475,8 @@ static std::vector s_Preset_print_options { ,"detect_narrow_internal_solid_infill" //Y21 ,"seam_gap" + //w16 + , "top_one_wall_type" }; static std::vector s_Preset_filament_options { diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 54732a3..db1156d 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -236,6 +236,12 @@ static t_config_enum_values s_keys_map_PerimeterGeneratorType { }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PerimeterGeneratorType) +//w16 +static t_config_enum_values s_keys_map_TopOneWallType{{"not apply", int(TopOneWallType::None)}, + {"all top", int(TopOneWallType::Alltop)}, + {"Only top most", int(TopOneWallType::Onlytopmost)}}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(TopOneWallType) + static void assign_printer_technology_to_unknown(t_optiondef_map &options, PrinterTechnology printer_technology) { for (std::pair &kvp : options) @@ -3531,6 +3537,19 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionEnum(PerimeterGeneratorType::Arachne)); + //w16 + def = this->add("top_one_wall_type", coEnum); + def->label = L("Only one wall on top surfaces"); + def->category = L("Layers and Perimeters"); + def->tooltip = L("Use only one wall on flat top surface, to give more space to the top infill pattern. Could be applyed on topmost surface or all top surface."); + def->set_enum({ + { "none", L("None") }, + { "Only top most", L("Onlytopmost") }, + { "all top", L("Alltop") } + }); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionEnum(TopOneWallType::None)); + def = this->add("wall_transition_length", coFloatOrPercent); def->label = L("Perimeter transition length"); def->category = L("Advanced"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 3c6b6ab..af9aad5 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -144,6 +144,9 @@ enum class PerimeterGeneratorType //B3 enum class GCodeThumbnailsFormat { QIDI,PNG, JPG, QOI }; +//w16 +enum class TopOneWallType { None, Alltop, Onlytopmost }; + #define CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(NAME) \ template<> const t_config_enum_names& ConfigOptionEnum::get_enum_names(); \ template<> const t_config_enum_values& ConfigOptionEnum::get_enum_values(); @@ -587,6 +590,8 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, wipe_into_objects)) //w11 ((ConfigOptionBool, detect_narrow_internal_solid_infill)) + //w16 + ((ConfigOptionEnum, top_one_wall_type)) ) PRINT_CONFIG_CLASS_DEFINE( diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 48c9dcc..f85d328 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -864,12 +864,13 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "wall_transition_angle" || opt_key == "wall_distribution_count" || opt_key == "min_feature_size" - || opt_key == "min_bead_width") { + || opt_key == "min_bead_width" + //w15 + || opt_key == "top_one_wall_type"){ steps.emplace_back(posSlice); } else if ( opt_key == "seam_position" //Y21 - || opt_key == "seam_gap" || opt_key == "seam_preferred_direction" || opt_key == "seam_preferred_direction_jitter" || opt_key == "support_material_speed" diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 4b42350..35b55d7 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -244,7 +244,6 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) { bool have_perimeters = config->opt_int("perimeters") > 0; for (auto el : { "extra_perimeters","extra_perimeters_on_overhangs", "thin_walls", "overhangs", -//Y21 "seam_position","seam_gap","staggered_inner_seams", "external_perimeters_first", "external_perimeter_extrusion_width", "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "enable_dynamic_overhang_speeds"}) toggle_field(el, have_perimeters); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 0636386..ff4ba14 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1467,6 +1467,8 @@ void TabPrint::build() optgroup->append_single_option_line("external_perimeters_first", category_path + "external-perimeters-first"); optgroup->append_single_option_line("gap_fill_enabled", category_path + "fill-gaps"); optgroup->append_single_option_line("perimeter_generator"); + //w16 + optgroup->append_single_option_line("top_one_wall_type"); optgroup = page->new_optgroup(L("Fuzzy skin (experimental)")); category_path = "fuzzy-skin_246186/#";