2550{
2552
2553
2556
2557
2562
2564
2565
2567
2568
2570
2571
2572
2575 }
2576
2578
2580
2582
2583
2584
2587
2588
2589
2590
2591
2592
2593 struct ArachneSurfaceResult
2594 {
2595 ExtrusionEntityCollection loops;
2596 bool has_loops = false;
2599 };
2600 std::vector<ArachneSurfaceResult> results(all_surfaces.size());
2601 tbb::parallel_for(tbb::blocked_range<size_t>(0, all_surfaces.size()), [&](
const tbb::blocked_range<size_t> &
range) {
2602 for (size_t surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) {
2603 const Surface &surface = all_surfaces[surface_idx];
2604 ArachneSurfaceResult &result = results[surface_idx];
2605 coord_t bead_width_0 = ext_perimeter_spacing;
2606
2607 int loop_number = this->config->wall_loops + surface.extra_perimeters - 1;
2608 int sparse_infill_density = this->config->sparse_infill_density.value;
2609 if (this->config->alternate_extra_wall && this->layer_id % 2 == 1 && !m_spiral_vase && sparse_infill_density > 0)
2610 loop_number++;
2611
2612
2613 const bool is_bottom_layer = (this->layer_id == object_config->raft_layers) ? true : false;
2614 if (is_bottom_layer && only_one_wall_first_layer)
2615 loop_number = 0;
2616
2617
2618 const bool is_topmost_layer = (this->upper_slices == nullptr) ? true : false;
2619 if (is_topmost_layer && loop_number > 0 && only_one_wall_top)
2620 loop_number = 0;
2621
2622 auto apply_precise_outer_wall = config->precise_outer_wall && config->wall_sequence == WallSequence::InnerOuter;
2623
2624 ExPolygons last = offset_ex(surface.expolygon.simplify_p(surface_simplify_resolution),
2625 apply_precise_outer_wall? -float(ext_perimeter_width - ext_perimeter_spacing )
2626 : -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
2627
2628 Arachne::WallToolPathsParams input_params = Arachne::make_paths_params(this->layer_id, *object_config, *print_config);
2629
2630 input_params.is_top_or_bottom_layer = (is_bottom_layer || is_topmost_layer) ? true : false;
2631
2632 coord_t wall_0_inset = 0;
2633 if (apply_precise_outer_wall)
2634 wall_0_inset = -coord_t(ext_perimeter_width / 2 - ext_perimeter_spacing / 2);
2635
2636
2637 ExPolygons top_expolygons;
2638
2639 const int inner_loop_number = (only_one_wall_top && upper_slices != nullptr) ? loop_number - 1 : -1;
2640
2641
2642 if (only_one_wall_top && loop_number > 0)
2643 loop_number = 0;
2644
2645 Arachne::WallToolPathsParams input_params_tmp = input_params;
2646
2647 Polygons last_p = to_polygons(last);
2648 Arachne::WallToolPaths wallToolPaths(last_p, bead_width_0, perimeter_spacing, coord_t(loop_number + 1),
2649 wall_0_inset, layer_height, input_params_tmp);
2650 std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
2651 ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
2652
2653
2654
2655 if (inner_loop_number >= 0) {
2656 assert(upper_slices != nullptr);
2657
2658 coord_t perimeter_width = this->perimeter_flow.scaled_width();
2659
2660
2661
2662 const float top_surface_min_width = std::max<float>(float(ext_perimeter_spacing) / 4.f + scaled<float>(0.00001), float(scale_(config->min_width_top_surface.get_abs_value(unscale_(perimeter_width)))) / 4.f);
2663
2664
2665
2666 auto get_top_expolygons = [&](const ExPolygons &contour, bool &uncovered) {
2667
2668 BoundingBox contour_bbox = get_extents(contour);
2669 contour_bbox.offset(SCALED_EPSILON);
2670
2671 Polygons upper_slices_clipped;
2672 if (object_config->interface_shells) {
2673 auto upper_slicer_same_region = to_expolygons(this->upper_slices_same_region->surfaces);
2674 upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(upper_slicer_same_region, contour_bbox);
2675 } else
2676 upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*upper_slices, contour_bbox);
2677
2678 ExPolygons top = diff_ex(contour, upper_slices_clipped);
2679 uncovered = !top.empty();
2680 if (top.empty())
2681 return top;
2682
2683 if (lower_slices != nullptr) {
2684 const float bridge_offset = float(std::max<coord_t>(ext_perimeter_spacing, perimeter_width));
2685 const Polygons lower_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, contour_bbox);
2686 const ExPolygons current_slices_bridges = offset_ex(diff_ex(top, lower_slices_clipped), bridge_offset);
2687
2688
2689 top = diff_ex(top, current_slices_bridges);
2690 }
2691
2692
2693
2694
2695
2696 top = offset2_ex(top, -top_surface_min_width, top_surface_min_width + float(perimeter_width * 0.85));
2697
2698
2699 return intersection_ex(top, contour);
2700 };
2701
2702
2703 std::vector<Arachne::VariableWidthLines> full_perimeters;
2704 Polygons full_inner_contour;
2705 bool full_perimeters_generated = false;
2706 auto generate_full_perimeters = [&]() {
2707 if (full_perimeters_generated)
2708 return;
2709 Arachne::WallToolPaths full_tool_paths(last_p, bead_width_0, perimeter_spacing, coord_t(inner_loop_number + 2), wall_0_inset, layer_height, input_params_tmp);
2710 full_perimeters = full_tool_paths.getToolPaths();
2711 full_inner_contour = full_tool_paths.getInnerContour();
2712 full_perimeters_generated = true;
2713 };
2714
2715
2716
2717
2718
2719
2720
2721 const coord_t outer_wall_tolerance = bead_width_0 / 10;
2722 if (widest_bead(perimeters) > bead_width_0 + 2 * outer_wall_tolerance) {
2723
2724 const ExPolygons nominal_infill_contour = offset_ex(last, -float(bead_width_0 + wall_0_inset));
2725 bool nominal_uncovered = false;
2726
2727 const ExPolygons top_zone = offset_ex(get_top_expolygons(nominal_infill_contour, nominal_uncovered), float(bead_width_0));
2728 if (nominal_uncovered) {
2729 generate_full_perimeters();
2730 if (! full_perimeters.empty() && ! full_perimeters.front().empty() &&
2731 length_off_reference(perimeters, full_perimeters.front(), top_zone, outer_wall_tolerance) > double(perimeter_width)) {
2732 perimeters = { full_perimeters.front() };
2733 infill_contour = diff_ex(nominal_infill_contour, walls_footprint(full_perimeters.front()), ApplySafetyOffset::Yes);
2734 }
2735 }
2736 }
2737
2738 bool uncovered = false;
2739 top_expolygons = get_top_expolygons(infill_contour, uncovered);
2740
2741 if (uncovered) {
2742
2743
2744
2745
2746 const bool clip_walls_over_top = top_fill_replaces_inner_walls(*this->config);
2747 const Polygons inner_region = to_polygons(offset_ex(clip_walls_over_top ? infill_contour
2748 : diff_ex(infill_contour, top_expolygons),
2749 wall_0_inset));
2750 Arachne::WallToolPaths inner_wall_tool_paths(inner_region, perimeter_spacing, perimeter_spacing, coord_t(inner_loop_number + 1), 0, layer_height, input_params_tmp);
2751 std::vector<Arachne::VariableWidthLines> inner_perimeters = inner_wall_tool_paths.getToolPaths();
2752
2753 if (clip_walls_over_top) {
2754 Polygons kept_over_top;
2755 clip_inner_walls_over_top(inner_perimeters, top_expolygons, perimeter_width, kept_over_top);
2756
2757 if (! kept_over_top.empty())
2758 top_expolygons = diff_ex(top_expolygons, kept_over_top);
2759 }
2760
2761
2762 if (!perimeters.empty())
2763 for (Arachne::VariableWidthLines &inner_perimeter : inner_perimeters)
2764 for (Arachne::ExtrusionLine &el : inner_perimeter)
2765 ++el.inset_idx;
2766
2767 perimeters.insert(perimeters.end(), inner_perimeters.begin(), inner_perimeters.end());
2768 infill_contour = union_ex(top_expolygons, inner_wall_tool_paths.getInnerContour());
2769 } else {
2770
2771
2772 generate_full_perimeters();
2773 perimeters = std::move(full_perimeters);
2774 infill_contour = union_ex(full_inner_contour);
2775 }
2776 }
2777
2778
2779 loop_number = int(perimeters.size()) - 1;
2780
2781 #ifdef ARACHNE_DEBUG
2782 {
2783 static int iRun = 0;
2784 export_perimeters_to_svg(debug_out_path("arachne-perimeters-%d-%d.svg", layer_id, iRun++), to_polygons(last), perimeters, union_ex(wallToolPaths.getInnerContour()));
2785 }
2786 #endif
2787
2788
2789
2790
2791 assert([&perimeters = std::as_const(perimeters)]() -> bool {
2792 for (const Arachne::VariableWidthLines& perimeter : perimeters)
2793 for (const Arachne::ExtrusionLine& el : perimeter)
2794 if (el.is_closed && el.junctions.front().p != el.junctions.back().p)
2795 return false;
2796 return true;
2797 }());
2798
2799 int start_perimeter = int(perimeters.size()) - 1;
2800 int end_perimeter = -1;
2801 int direction = -1;
2802
2803 bool is_outer_wall_first =
2804 this->config->wall_sequence == WallSequence::OuterInner ||
2805 this->config->wall_sequence == WallSequence::InnerOuterInner;
2806
2807 if (layer_id == 0){
2808 is_outer_wall_first =
2809 this->config->wall_sequence == WallSequence::OuterInner || this->outer_walls_first_for_brim();
2810 }
2811 if (is_outer_wall_first) {
2812 start_perimeter = 0;
2813 end_perimeter = int(perimeters.size());
2814 direction = 1;
2815 }
2816
2817 std::vector<Arachne::ExtrusionLine*> all_extrusions;
2818 for (int perimeter_idx = start_perimeter; perimeter_idx != end_perimeter; perimeter_idx += direction) {
2819 if (perimeters[perimeter_idx].empty())
2820 continue;
2821 for (Arachne::ExtrusionLine& wall : perimeters[perimeter_idx])
2822 all_extrusions.emplace_back(&wall);
2823 }
2824
2825
2826 std::vector<size_t> blocked(all_extrusions.size(), 0);
2827 std::vector<std::vector<size_t>> blocking(all_extrusions.size());
2828 std::unordered_map<const Arachne::ExtrusionLine*, size_t> map_extrusion_to_idx;
2829 for (size_t idx = 0; idx < all_extrusions.size(); idx++)
2830 map_extrusion_to_idx.emplace(all_extrusions[idx], idx);
2831
2832 auto extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, is_outer_wall_first);
2833 for (auto [before, after] : extrusions_constrains) {
2834 auto after_it = map_extrusion_to_idx.find(after);
2835 ++blocked[after_it->second];
2836 blocking[map_extrusion_to_idx.find(before)->second].emplace_back(after_it->second);
2837 }
2838
2839 std::vector<bool> processed(all_extrusions.size(), false);
2840 Point current_position = all_extrusions.empty() ? Point::Zero() : all_extrusions.front()->junctions.front().p;
2841 std::vector<PerimeterGeneratorArachneExtrusion> ordered_extrusions;
2842 ordered_extrusions.reserve(all_extrusions.size());
2843
2844 while (ordered_extrusions.size() < all_extrusions.size()) {
2845 size_t best_candidate = 0;
2846 double best_distance_sqr = std::numeric_limits<double>::max();
2847 bool is_best_closed = false;
2848
2849 std::vector<size_t> available_candidates;
2850 for (size_t candidate = 0; candidate < all_extrusions.size(); ++candidate) {
2851 if (processed[candidate] || blocked[candidate])
2852 continue;
2853 available_candidates.push_back(candidate);
2854 }
2855
2856 std::sort(available_candidates.begin(), available_candidates.end(), [&all_extrusions](const size_t a_idx, const size_t b_idx) -> bool {
2857 return all_extrusions[a_idx]->is_closed < all_extrusions[b_idx]->is_closed;
2858 });
2859
2860 for (const size_t candidate_path_idx : available_candidates) {
2861 auto& path = all_extrusions[candidate_path_idx];
2862
2863 if (path->junctions.empty()) {
2864 if (best_distance_sqr == std::numeric_limits<double>::max()) {
2865 best_candidate = candidate_path_idx;
2866 is_best_closed = path->is_closed;
2867 }
2868 continue;
2869 }
2870
2871 const Point candidate_position = path->junctions.front().p;
2872 double distance_sqr = (current_position - candidate_position).cast<double>().norm();
2873 if (distance_sqr < best_distance_sqr) {
2874 if (path->is_closed || (!path->is_closed && best_distance_sqr != std::numeric_limits<double>::max()) || (!path->is_closed && !is_best_closed)) {
2875 best_candidate = candidate_path_idx;
2876 best_distance_sqr = distance_sqr;
2877 is_best_closed = path->is_closed;
2878 }
2879 }
2880 }
2881
2882 auto& best_path = all_extrusions[best_candidate];
2883 ordered_extrusions.push_back({ best_path, best_path->is_contour() });
2884 processed[best_candidate] = true;
2885 for (size_t unlocked_idx : blocking[best_candidate])
2886 blocked[unlocked_idx]--;
2887
2888 if (!best_path->junctions.empty()) {
2889 if (best_path->is_closed)
2890 current_position = best_path->junctions[0].p;
2891 else
2892 current_position = best_path->junctions.back().p;
2893 }
2894 }
2895
2896
2897 if (this->config->wall_sequence == WallSequence::InnerOuterInner && layer_id > 0) {
2898 if (ordered_extrusions.size() > 2) {
2899 int position = 0;
2900 int arr_i, arr_j = 0;
2901 int outer, first_internal, second_internal, max_internal, current_perimeter;
2902
2903
2904
2905 bringContoursToFront(ordered_extrusions);
2906 std::vector<PerimeterGeneratorArachneExtrusion> reordered_extrusions;
2907
2908
2909
2910
2911
2912
2913 coord_t threshold_external = (apply_precise_outer_wall)
2914
2915 ? ( this->ext_perimeter_flow.scaled_spacing()
2916 + this->perimeter_flow.scaled_spacing()/2.0 )
2917
2918 : ( this->ext_perimeter_flow.scaled_spacing()/2.0
2919 + this->perimeter_flow.scaled_spacing()/2.0 );
2920
2921
2922 coord_t threshold_internal = this->perimeter_flow.scaled_spacing();
2923
2924
2925
2926 ordered_extrusions = reorderPerimetersByProximity(ordered_extrusions,threshold_external,threshold_internal );
2927 reordered_extrusions = ordered_extrusions;
2928
2929
2930
2931
2932
2933 while (position < reordered_extrusions.size()) {
2934 outer = first_internal = second_internal = current_perimeter = -1;
2935 max_internal = reordered_extrusions.size()-1;
2936
2937
2938
2939
2940 for (arr_i = position; arr_i < reordered_extrusions.size(); ++arr_i) {
2941
2942 switch (reordered_extrusions[arr_i].extrusion->inset_idx) {
2943 case 0:
2944 if (outer == -1)
2945 outer = arr_i;
2946 break;
2947 case 1:
2948 if (first_internal==-1 && arr_i>outer && outer!=-1){
2949 first_internal = arr_i;
2950 }
2951 break;
2952 case 2:
2953 if (second_internal == -1 && arr_i > first_internal && outer!=-1){
2954 second_internal = arr_i;
2955 }
2956 break;
2957 }
2958 if(outer >-1 && first_internal>-1 && reordered_extrusions[arr_i].extrusion->inset_idx == 0){
2959
2960 arr_i=arr_i-1;
2961 max_internal = arr_i;
2962 break;
2963 }
2964 }
2965
2966
2967 if (outer > -1 && first_internal > -1 && second_internal > -1) {
2968 std::vector<PerimeterGeneratorArachneExtrusion> inner_outer_extrusions;
2969 inner_outer_extrusions.resize(max_internal - position + 1);
2970
2971
2972 for (arr_j = max_internal; arr_j >=position; --arr_j){
2973 if(arr_j >= second_internal){
2974
2975 inner_outer_extrusions[max_internal-arr_j] = reordered_extrusions[arr_j];
2976 current_perimeter++;
2977 }
2978 }
2979
2980 for (arr_j = position; arr_j < second_internal; ++arr_j){
2981
2982 inner_outer_extrusions[++current_perimeter] = reordered_extrusions[arr_j];
2983 }
2984
2985 for(arr_j = position; arr_j <= max_internal; ++arr_j)
2986 ordered_extrusions[arr_j] = inner_outer_extrusions[arr_j-position];
2987 }
2988
2989 position = arr_i + 1;
2990 }
2991 }
2992 }
2993
2994 bool steep_overhang_contour = false;
2995 bool steep_overhang_hole = false;
2996 if (!config->overhang_reverse) {
2997
2998 steep_overhang_contour = true;
2999 steep_overhang_hole = true;
3000 }
3001 if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions, steep_overhang_contour, steep_overhang_hole); !extrusion_coll.empty()) {
3002 if (config->overhang_reverse) {
3003 reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole,
3004 this->config->overhang_reverse_internal_only);
3005 }
3006 defer_unsupported_loops(*this, extrusion_coll);
3007 result.loops = std::move(extrusion_coll);
3008 result.has_loops = true;
3009 }
3010
3011 const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
3012
3013 if (offset_ex(infill_contour, -float(spacing / 2.)).empty())
3014 infill_contour.clear();
3015
3016
3017
3018
3019
3020 coord_t inset =
3021 (loop_number < 0) ? 0 :
3022 (loop_number == 0) ?
3023
3024 ext_perimeter_spacing :
3025
3026 perimeter_spacing;
3027 coord_t top_inset = inset;
3028
3029 top_inset = coord_t(scale_(this->config->top_bottom_infill_wall_overlap.get_abs_value(unscale<double>(inset))));
3030 if(is_topmost_layer || is_bottom_layer)
3031 inset = coord_t(scale_(this->config->top_bottom_infill_wall_overlap.get_abs_value(unscale<double>(inset))));
3032 else
3033 inset = coord_t(scale_(this->config->infill_wall_overlap.get_abs_value(unscale<double>(inset))));
3034
3035
3036 Polygons pp;
3037 for (ExPolygon& ex : infill_contour)
3038 ex.simplify_p(m_scaled_resolution, &pp);
3039 ExPolygons not_filled_exp = union_ex(pp);
3040
3041 const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
3042
3043 ExPolygons infill_exp = offset2_ex(
3044 not_filled_exp,
3045 float(-min_perimeter_infill_spacing / 2.),
3046 float(inset + min_perimeter_infill_spacing / 2.));
3047
3048 if (!top_expolygons.empty()) {
3049 infill_exp = union_ex(infill_exp, offset_ex(top_expolygons, double(top_inset)));
3050 }
3051 result.infill = std::move(infill_exp);
3052
3053
3054 {
3055 ExPolygons polyWithoutOverlap;
3056 polyWithoutOverlap = offset2_ex(
3057 not_filled_exp,
3058 float(-min_perimeter_infill_spacing / 2.),
3059 float(+min_perimeter_infill_spacing / 2.));
3060 if (!top_expolygons.empty())
3061 polyWithoutOverlap = union_ex(polyWithoutOverlap, top_expolygons);
3062 result.no_overlap = std::move(polyWithoutOverlap);
3063 }
3064 }
3065 });
3066 for (ArachneSurfaceResult &result : results) {
3067 if (result.has_loops)
3068
3069 this->
loops->append(std::move(result.loops));
3073 }
3074}
bool has_fuzzy_skin
Definition PerimeterGenerator.hpp:115
void apply_extra_perimeters(ExPolygons &infill_area)
Definition PerimeterGenerator.cpp:1333
void process_no_bridge(Surfaces &all_surfaces, coord_t perimeter_spacing, coord_t ext_perimeter_width)
Definition PerimeterGenerator.cpp:2138
void group_region_by_fuzzify(PerimeterGenerator &g)
Definition FuzzySkin.cpp:430
static bool has_bottom_shell_layers(const PrintRegionConfig &config)
Definition PerimeterGenerator.cpp:628
std::vector< ExPolygon > ExPolygons
Definition ExPolygon.hpp:20
@ Enabled
Definition PrintConfig.hpp:240
auto range(Cont &&cont)
Definition libslic3r.h:405
static bool has_top_shell_layers(const PrintRegionConfig &config)
Definition PerimeterGenerator.cpp:619
std::vector< Surface > Surfaces
Definition Surface.hpp:123