2481{
2482
2483 assert(params.anchor_length >= 0.);
2484 assert(params.anchor_length_max >= 0.01f);
2485 assert(params.anchor_length_max >= params.anchor_length);
2486
2488
2489#ifdef INFILL_DEBUG_OUTPUT
2490 static int iRun = 0;
2491 ++ iRun;
2492 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-initial-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2493#endif
2494
2496 const double line_spacing =
scale_(
spacing) / params.density;
2497 const double min_arch_length = 1.3 * line_spacing;
2498 const double trim_length = line_half_width * 0.3;
2499
2500
2501
2502
2504
2505#ifdef INFILL_DEBUG_OUTPUT
2506 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-marked-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2507#endif
2508
2509
2510
2511 {
2512 std::vector<size_t> num_boundary_contour_infill_points(graph.boundary.size(), 0);
2513 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary)
2514 ++ num_boundary_contour_infill_points[cp.contour_idx];
2515 for (size_t i = 0; i < num_boundary_contour_infill_points.size(); ++ i)
2516 if (num_boundary_contour_infill_points[i] == 0 && graph.boundary_params[i].back() > trim_length + 0.5 * line_spacing) {
2517
2518 Polyline pl(graph.boundary[i]);
2519 pl.points.emplace_back(pl.points.front());
2520 pl.clip_end(trim_length);
2521 if (pl.size() > 1)
2522 polylines_out.emplace_back(std::move(pl));
2523 }
2524 }
2525
2526
2527
2528 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2529 if (cp.next_on_contour && cp.next_trimmed && cp.next_on_contour->prev_trimmed) {
2530
2531
2532
2533 bool first = graph.first(cp);
2536 if (first) {
2537 left += line_half_width;
2538 right += line_spacing - line_half_width;
2539 } else {
2540 left -= line_spacing - line_half_width;
2541 right -= line_half_width;
2542 }
2543 double param_start = cp.param + cp.contour_not_taken_length_next;
2544 double param_end = cp.next_on_contour->param - cp.next_on_contour->contour_not_taken_length_prev;
2545 double contour_length = graph.boundary_params[cp.contour_idx].back();
2546 if (param_start >= contour_length)
2547 param_start -= contour_length;
2548 if (param_end < 0)
2549 param_end += contour_length;
2550
2552 emit_loops_in_band(left, right, graph.boundary[cp.contour_idx], graph.boundary_params[cp.contour_idx], param_start, param_end, 0.5 * line_spacing, polylines_out);
2553 }
2554 }
2555#ifdef INFILL_DEBUG_OUTPUT
2556 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-excess-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2557#endif
2558
2560
2561#ifdef INFILL_DEBUG_OUTPUT
2562 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-extended-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2563#endif
2564
2565 std::vector<size_t> merged_with(infill_ordered.size());
2566 std::iota(merged_with.begin(), merged_with.end(), 0);
2567 auto get_and_update_merged_with = [&graph, &merged_with](const ContourIntersectionPoint *cp) -> size_t {
2568 size_t polyline_idx = (cp - graph.map_infill_end_point_to_boundary.data()) / 2;
2569 for (size_t last = polyline_idx;;) {
2570 size_t lower = merged_with[last];
2571 assert(lower <= last);
2572 if (lower == last) {
2573 merged_with[polyline_idx] = last;
2574 return last;
2575 }
2576 last = lower;
2577 }
2578 assert(false);
2579 return std::numeric_limits<size_t>::max();
2580 };
2581
2584 };
2585
2586 auto take_vertical_prev = [](const ContourIntersectionPoint &cp) {
2587 return cp.prev_trimmed == cp.next_trimmed ?
2588
2589 cp.contour_not_taken_length_prev > cp.contour_not_taken_length_next :
2590
2591 ! cp.prev_trimmed && cp.next_trimmed;
2592 };
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603 auto take_next = [&graph, &infill_ordered, &merged_with, get_and_update_merged_with, line_half_width, trim_length](ContourIntersectionPoint &cp, bool take_first) {
2604
2605 ContourIntersectionPoint *cp1 = &cp;
2606 ContourIntersectionPoint *cp2 = cp.next_on_contour;
2607 assert(cp1->next_trimmed == cp2->prev_trimmed);
2608
2609 if (take_first ? cp1->consumed : cp2->consumed)
2610 return;
2611 size_t polyline_idx1 = get_and_update_merged_with(cp1);
2612 size_t polyline_idx2 = get_and_update_merged_with(cp2);
2613 Polyline &polyline1 = infill_ordered[polyline_idx1];
2614 Polyline &polyline2 = infill_ordered[polyline_idx2];
2616 const std::vector<double> &contour_params = graph.boundary_params[cp1->contour_idx];
2617 assert(cp1->consumed ||
contour[cp1->point_idx] == polyline1.
points.front() ||
contour[cp1->point_idx] == polyline1.
points.back());
2618 assert(cp2->consumed ||
contour[cp2->point_idx] == polyline2.
points.front() ||
contour[cp2->point_idx] == polyline2.
points.back());
2619 bool trimmed = take_first ? cp1->next_trimmed : cp2->prev_trimmed;
2620 if (! trimmed) {
2621
2622 trimmed = cp1 == cp2 || polyline_idx1 == polyline_idx2 || (take_first ? cp2->consumed : cp1->consumed);
2623 if (! trimmed) {
2624 const bool cp1_first = ((cp1 - graph.map_infill_end_point_to_boundary.data()) & 1) == 0;
2625 const ContourIntersectionPoint* cp1_other = cp1_first ? cp1 + 1 : cp1 - 1;
2626
2627 trimmed = cp2 == cp1_other;
2628 }
2629 if (trimmed) {
2630
2631
2632
2634 if (take_first) {
2635 cp1->trim_next(std::max(0., len - trim_length -
SCALED_EPSILON));
2636 cp2->trim_prev(0);
2637 } else {
2638 cp1->trim_next(0);
2639 cp2->trim_prev(std::max(0., len - trim_length -
SCALED_EPSILON));
2640 }
2641 }
2642 }
2643 if (trimmed) {
2644 if (take_first)
2646 else
2648 } else if (! cp1->consumed && ! cp2->consumed) {
2650 polyline1.reverse();
2652 polyline2.reverse();
2653 take(polyline1, polyline2,
contour, cp1, cp2,
false);
2654
2655 if (polyline_idx2 < polyline_idx1) {
2656 polyline2 = std::move(polyline1);
2657 polyline1.points.clear();
2658 merged_with[polyline_idx1] = merged_with[polyline_idx2];
2659 } else {
2660 polyline2.points.clear();
2661 merged_with[polyline_idx2] = merged_with[polyline_idx1];
2662 }
2663 }
2664 };
2665
2666
2667
2668 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2670 if (cp.consumed)
2671 continue;
2672 const ContourIntersectionPoint &cp_other = graph.other(cp);
2673 assert((cp.next_on_contour == &cp_other) == (cp_other.prev_on_contour == &cp));
2674 assert((cp.prev_on_contour == &cp_other) == (cp_other.next_on_contour == &cp));
2677
2678 assert((cp.next_on_contour == &cp) == (cp.prev_on_contour == &cp));
2679 bool can_take_prev = vertical(dir_prev) && ! cp.prev_on_contour->consumed && cp.prev_on_contour != &cp_other;
2680 bool can_take_next = vertical(dir_next) && ! cp.next_on_contour->consumed && cp.next_on_contour != &cp_other;
2681 if (can_take_prev && (! can_take_next || take_vertical_prev(cp))) {
2682 if (! cp.prev_trimmed || cp.contour_not_taken_length_prev > min_arch_length)
2683
2684 take_next(*cp.prev_on_contour, false);
2685 } else if (can_take_next) {
2686 if (! cp.next_trimmed || cp.contour_not_taken_length_next > min_arch_length)
2687
2688 take_next(cp, true);
2689 }
2690 }
2691
2692#ifdef INFILL_DEBUG_OUTPUT
2693 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-vertical-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2694#endif
2695
2697
2698
2699 const double cost_low = line_spacing * 1.3;
2700 const double cost_high = line_spacing * 2.;
2701 const double cost_veryhigh = line_spacing * 3.;
2702
2703 {
2704 std::vector<const SupportArcCost*> selected;
2705 selected.reserve(graph.map_infill_end_point_to_boundary.size());
2706 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2707 if (cp.consumed)
2708 continue;
2709 const SupportArcCost &cost_prev = arches[(&cp - graph.map_infill_end_point_to_boundary.data()) * 2];
2710 const SupportArcCost &cost_next = *(&cost_prev + 1);
2711 double cost_min = cost_prev.cost;
2712 double cost_max = cost_next.cost;
2713 if (cost_min > cost_max)
2714 std::swap(cost_min, cost_max);
2715 if (cost_max < cost_low || cost_min > cost_high)
2716
2717 continue;
2718 const double cost_diff_relative = (cost_max - cost_min) / cost_max;
2719 if (cost_diff_relative < 0.25)
2720
2721 continue;
2722 if (cost_prev.cost > cost_low)
2723 selected.emplace_back(&cost_prev);
2724 if (cost_next.cost > cost_low)
2725 selected.emplace_back(&cost_next);
2726 }
2727
2728 std::sort(selected.begin(), selected.end(), [](const auto *l, const auto *r) { return l->cost > r->cost; });
2729
2730 for (const SupportArcCost *arc : selected) {
2731 ContourIntersectionPoint &cp = graph.map_infill_end_point_to_boundary[(
arc - arches.data()) / 2];
2732 if (! cp.consumed) {
2733 bool prev = ((
arc - arches.data()) & 1) == 0;
2734 if (prev)
2735 take_next(*cp.prev_on_contour, false);
2736 else
2737 take_next(cp, true);
2738 }
2739 }
2740 }
2741
2742#if 0
2743 {
2744
2745
2746 struct Arc {
2748 double arc_length;
2749 bool take_next;
2750 };
2751 std::vector<Arc> arches;
2752 arches.reserve(graph.map_infill_end_point_to_boundary.size());
2753 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2754 if (cp.consumed)
2755 continue;
2756
2757 assert(cp.next_on_contour != &cp);
2758 const bool first = ((&cp - graph.map_infill_end_point_to_boundary.data()) & 1) == 0;
2759 const ContourIntersectionPoint *other_end = first ? &cp + 1 : &cp - 1;
2760 const bool loop_next = cp.next_on_contour == other_end;
2761 if (! loop_next && cp.could_connect_next()) {
2762 if (cp.contour_not_taken_length_next > min_arch_length) {
2763
2764 arches.push_back({ &cp, cp.contour_not_taken_length_next, true });
2765 arches.push_back({ cp.next_on_contour, cp.contour_not_taken_length_next, false });
2766 }
2767 } else {
2768
2769 if (cp.prev_trimmed && cp.could_take_prev()) {
2770
2771
2772 const Point &pt = graph.point(cp);
2773 const Point &prev = graph.point(*cp.prev_on_contour);
2774 if (std::abs(pt.x() - prev.x()) <
coord_t(0.5 * line_spacing)) {
2775
2776
2777 if (cp.contour_not_taken_length_prev > 0.5 * line_spacing)
2778 arches.push_back({ &cp, cp.contour_not_taken_length_prev, false });
2779 } else {
2780
2781 if (cp.contour_not_taken_length_prev > min_arch_length)
2782 arches.push_back({ &cp, cp.contour_not_taken_length_prev, false });
2783 }
2784 }
2785 if (cp.next_trimmed && cp.could_take_next()) {
2786
2787 const Point &pt = graph.point(cp);
2788 const Point &next = graph.point(*cp.next_on_contour);
2789 if (std::abs(pt.x() - next.x()) <
coord_t(0.5 * line_spacing)) {
2790
2791
2792 if (cp.contour_not_taken_length_next > 0.5 * line_spacing)
2793 arches.push_back({ &cp, cp.contour_not_taken_length_next, true });
2794 } else {
2795
2796 if (cp.contour_not_taken_length_next > min_arch_length)
2797 arches.push_back({ &cp, cp.contour_not_taken_length_next, true });
2798 }
2799 }
2800 }
2801 }
2802
2803 std::sort(arches.begin(), arches.end(), [](const auto &l, const auto &r) { return l.arc_length > r.arc_length; });
2804
2805 for (Arc &arc : arches)
2807 take_next(*
arc.intersection,
true);
2808 else
2809 take_next(*
arc.intersection->prev_on_contour,
false);
2810 }
2811#endif
2812
2813#ifdef INFILL_DEBUG_OUTPUT
2814 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-arches-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2815#endif
2816
2817
2818 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2820 if (cp.consumed)
2821 continue;
2822 bool first = ((&cp - graph.map_infill_end_point_to_boundary.data()) & 1) == 0;
2823 if (first) {
2824
2825 if (get_and_update_merged_with(&cp) != get_and_update_merged_with(cp.next_on_contour))
2826 take_next(cp, true);
2827 } else {
2828 if (get_and_update_merged_with(&cp) != get_and_update_merged_with(cp.prev_on_contour))
2829 take_next(*cp.prev_on_contour, false);
2830 }
2831 }
2832
2833#ifdef INFILL_DEBUG_OUTPUT
2834 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-zigzag-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2835#endif
2836
2837
2838 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2839 const bool first = ((&cp - graph.map_infill_end_point_to_boundary.data()) & 1) == 0;
2840 const ContourIntersectionPoint *other_end = first ? &cp + 1 : &cp - 1;
2841 const bool loop_next = cp.next_on_contour == other_end;
2842 const bool loop_prev = other_end->next_on_contour == &cp;
2843#ifndef NDEBUG
2844 const SupportArcCost &cost_prev = arches[(&cp - graph.map_infill_end_point_to_boundary.data()) * 2];
2845 const SupportArcCost &cost_next = *(&cost_prev + 1);
2846 assert(cost_prev.self_loop == loop_prev);
2847 assert(cost_next.self_loop == loop_next);
2848#endif
2849 if (loop_prev && cp.could_take_prev())
2850 take_next(*cp.prev_on_contour, false);
2851 if (loop_next && cp.could_take_next())
2852 take_next(cp, true);
2853 }
2854
2855#ifdef INFILL_DEBUG_OUTPUT
2856 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-caps-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2857#endif
2858
2859
2860 {
2861 std::vector<const SupportArcCost*> candidates;
2862 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2863 if (cp.could_take_prev())
2864 candidates.emplace_back(&arches[(&cp - graph.map_infill_end_point_to_boundary.data()) * 2]);
2865 if (cp.could_take_next())
2866 candidates.emplace_back(&arches[(&cp - graph.map_infill_end_point_to_boundary.data()) * 2 + 1]);
2867 }
2868 std::sort(candidates.begin(), candidates.end(), [](auto *c1, auto *c2) { return c1->cost > c2->cost; });
2869 for (const SupportArcCost *candidate : candidates) {
2870 ContourIntersectionPoint &cp = graph.map_infill_end_point_to_boundary[(candidate - arches.data()) / 2];
2871 bool prev = ((candidate - arches.data()) & 1) == 0;
2872 if (prev) {
2873 if (cp.could_take_prev() && (get_and_update_merged_with(&cp) != get_and_update_merged_with(cp.prev_on_contour) || candidate->cost > cost_high))
2874 take_next(*cp.prev_on_contour, false);
2875 } else {
2876 if (cp.could_take_next() && (get_and_update_merged_with(&cp) != get_and_update_merged_with(cp.next_on_contour) || candidate->cost > cost_high))
2877 take_next(cp, true);
2878 }
2879 }
2880 }
2881
2882#ifdef INFILL_DEBUG_OUTPUT
2883 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-Tjoints-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2884#endif
2885
2886
2887 const double cap_cost = 0.5 * line_spacing;
2888 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2889 const SupportArcCost &cost_prev = arches[(&cp - graph.map_infill_end_point_to_boundary.data()) * 2];
2890 const SupportArcCost &cost_next = *(&cost_prev + 1);
2892 (cost_prev.self_loop ?
2893 cost_prev.cost > cap_cost :
2894 cost_prev.cost > cost_veryhigh)) {
2895 assert(cp.consumed && (cp.prev_on_contour->consumed || cp.prev_trimmed));
2896 Polyline pl { graph.point(cp) };
2897 if (! cp.prev_trimmed) {
2898 cp.trim_prev(cp.contour_not_taken_length_prev - line_half_width);
2899 cp.prev_on_contour->trim_next(0);
2900 }
2902 take_cw_limited(pl, graph.boundary[cp.contour_idx], graph.boundary_params[cp.contour_idx], cp.point_idx, cp.prev_on_contour->point_idx, cp.contour_not_taken_length_prev);
2903 cp.trim_prev(0);
2904 pl.clip_start(line_half_width);
2905 polylines_out.emplace_back(std::move(pl));
2906 }
2907 }
2909 (cost_next.self_loop ?
2910 cost_next.cost > cap_cost :
2911 cost_next.cost > cost_veryhigh)) {
2912 assert(cp.consumed && (cp.next_on_contour->consumed || cp.next_trimmed));
2913 Polyline pl { graph.point(cp) };
2914 if (! cp.next_trimmed) {
2915 cp.trim_next(cp.contour_not_taken_length_next - line_half_width);
2916 cp.next_on_contour->trim_prev(0);
2917 }
2919 take_ccw_limited(pl, graph.boundary[cp.contour_idx], graph.boundary_params[cp.contour_idx], cp.point_idx, cp.next_on_contour->point_idx, cp.contour_not_taken_length_next);
2920 cp.trim_next(0);
2921 pl.clip_start(line_half_width);
2922 polylines_out.emplace_back(std::move(pl));
2923 }
2924 }
2925 }
2926
2927#ifdef INFILL_DEBUG_OUTPUT
2928 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-final-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2929#endif
2930
2931 polylines_out.reserve(polylines_out.size() + std::count_if(infill_ordered.begin(), infill_ordered.end(), [](const Polyline &pl) { return ! pl.empty(); }));
2932 for (Polyline &pl : infill_ordered)
2933 if (! pl.empty())
2934 polylines_out.emplace_back(std::move(pl));
2935}
Points points
Definition MultiPoint.hpp:22
#define SCALED_EPSILON
Definition libslic3r.h:109
#define scale_(val)
Definition libslic3r.h:106
std::string debug_out_path(const char *name,...)
Definition utils.cpp:382
static double take_ccw_limited(Polyline &pl, const Points &contour, const std::vector< double > ¶ms, size_t idx_start, size_t idx_end, double length_to_take)
Definition FillBase.cpp:601
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:769
static void take(Polyline &pl1, const Polyline &pl2, const Points &contour, size_t idx_start, size_t idx_end, bool clockwise)
Definition FillBase.cpp:642
double path_length_along_contour_ccw(const ContourIntersectionPoint *cp1, const ContourIntersectionPoint *cp2, double contour_length)
Definition FillBase.cpp:502
static void emit_loops_in_band(coord_t left, coord_t right, const Points &contour, const std::vector< double > &contour_params, double tbegin, double tend, double min_length, Polylines &polylines_out)
Definition FillBase.cpp:2188
BoundaryInfillGraph create_boundary_infill_graph(const Polylines &infill_ordered, const std::vector< const Polygon * > &boundary_src, const BoundingBox &bbox, const double spacing)
Definition FillBase.cpp:1543
static bool cyclic_interval_inside_interval(double outer_low, double outer_high, double inner_low, double inner_high, double length)
Definition FillBase.cpp:968
static constexpr auto boundary_idx_unconnected
Definition FillBase.cpp:1374
static void take_limited(Polyline &pl1, const Points &contour, const std::vector< double > ¶ms, ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise, double take_max_length, double line_half_width)
Definition FillBase.cpp:688
static std::vector< SupportArcCost > evaluate_support_arches(Polylines &infill, BoundaryInfillGraph &graph, const double spacing, const FillParams ¶ms)
Definition FillBase.cpp:2436
static double take_cw_limited(Polyline &pl, const Points &contour, const std::vector< double > ¶ms, size_t idx_start, size_t idx_end, double length_to_take)
Definition FillBase.cpp:545
static void mark_boundary_segments_overlapping_infill(BoundaryInfillGraph &graph, const Polylines &infill, const double spacing)
Definition FillBase.cpp:1463
const Polygon & contour(const ExPolygon &p)
Definition AGGRaster.hpp:30
static void base_support_extend_infill_lines(Polylines &infill, BoundaryInfillGraph &graph, const double spacing, const FillParams ¶ms)
Definition FillBase.cpp:2067
@ right
Definition MarchingSquares.hpp:190
@ left
Definition MarchingSquares.hpp:188
Direction
Definition FillBase.cpp:1409
@ Down
Definition FillBase.cpp:1413
@ Up
Definition FillBase.cpp:1412