2069{
2070
2071 assert(params.anchor_length >= 0.);
2072 assert(params.anchor_length_max >= 0.01f);
2073 assert(params.anchor_length_max >= params.anchor_length);
2074
2076
2077#ifdef INFILL_DEBUG_OUTPUT
2078 static int iRun = 0;
2079 ++ iRun;
2080 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-initial-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2081#endif
2082
2084 const double line_spacing =
scale_(
spacing) / params.density;
2085 const double min_arch_length = 1.3 * line_spacing;
2086 const double trim_length = line_half_width * 0.3;
2087
2088
2089
2090
2092
2093#ifdef INFILL_DEBUG_OUTPUT
2094 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-marked-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2095#endif
2096
2097
2098
2099 {
2100 std::vector<size_t> num_boundary_contour_infill_points(graph.boundary.size(), 0);
2101 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary)
2102 ++ num_boundary_contour_infill_points[cp.contour_idx];
2103 for (size_t i = 0; i < num_boundary_contour_infill_points.size(); ++ i)
2104 if (num_boundary_contour_infill_points[i] == 0 && graph.boundary_params[i].back() > trim_length + 0.5 * line_spacing) {
2105
2106 Polyline pl(graph.boundary[i]);
2107 pl.points.emplace_back(pl.points.front());
2108 pl.clip_end(trim_length);
2109 if (pl.size() > 1)
2110 polylines_out.emplace_back(std::move(pl));
2111 }
2112 }
2113
2114
2115
2116 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2117 if (cp.next_on_contour && cp.next_trimmed && cp.next_on_contour->prev_trimmed) {
2118
2119
2120
2121 bool first = graph.first(cp);
2124 if (first) {
2125 left += line_half_width;
2126 right += line_spacing - line_half_width;
2127 } else {
2128 left -= line_spacing - line_half_width;
2129 right -= line_half_width;
2130 }
2131 double param_start = cp.param + cp.contour_not_taken_length_next;
2132 double param_end = cp.next_on_contour->param - cp.next_on_contour->contour_not_taken_length_prev;
2133 double contour_length = graph.boundary_params[cp.contour_idx].back();
2134 if (param_start >= contour_length)
2135 param_start -= contour_length;
2136 if (param_end < 0)
2137 param_end += contour_length;
2138
2140 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);
2141 }
2142 }
2143#ifdef INFILL_DEBUG_OUTPUT
2144 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-excess-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2145#endif
2146
2148
2149#ifdef INFILL_DEBUG_OUTPUT
2150 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-extended-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2151#endif
2152
2153 std::vector<size_t> merged_with(infill_ordered.size());
2154 std::iota(merged_with.begin(), merged_with.end(), 0);
2155 auto get_and_update_merged_with = [&graph, &merged_with](const ContourIntersectionPoint *cp) -> size_t {
2156 size_t polyline_idx = (cp - graph.map_infill_end_point_to_boundary.data()) / 2;
2157 for (size_t last = polyline_idx;;) {
2158 size_t lower = merged_with[last];
2159 assert(lower <= last);
2160 if (lower == last) {
2161 merged_with[polyline_idx] = last;
2162 return last;
2163 }
2164 last = lower;
2165 }
2166 assert(false);
2167 return std::numeric_limits<size_t>::max();
2168 };
2169
2172 };
2173
2174 auto take_vertical_prev = [](const ContourIntersectionPoint &cp) {
2175 return cp.prev_trimmed == cp.next_trimmed ?
2176
2177 cp.contour_not_taken_length_prev > cp.contour_not_taken_length_next :
2178
2179 ! cp.prev_trimmed && cp.next_trimmed;
2180 };
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191 auto take_next = [&graph, &infill_ordered, &merged_with, get_and_update_merged_with, line_half_width, trim_length](ContourIntersectionPoint &cp, bool take_first) {
2192
2193 ContourIntersectionPoint *cp1 = &cp;
2194 ContourIntersectionPoint *cp2 = cp.next_on_contour;
2195 assert(cp1->next_trimmed == cp2->prev_trimmed);
2196
2197 if (take_first ? cp1->consumed : cp2->consumed)
2198 return;
2199 size_t polyline_idx1 = get_and_update_merged_with(cp1);
2200 size_t polyline_idx2 = get_and_update_merged_with(cp2);
2201 Polyline &polyline1 = infill_ordered[polyline_idx1];
2202 Polyline &polyline2 = infill_ordered[polyline_idx2];
2204 const std::vector<double> &contour_params = graph.boundary_params[cp1->contour_idx];
2205 assert(cp1->consumed ||
contour[cp1->point_idx] == polyline1.
points.front() ||
contour[cp1->point_idx] == polyline1.
points.back());
2206 assert(cp2->consumed ||
contour[cp2->point_idx] == polyline2.
points.front() ||
contour[cp2->point_idx] == polyline2.
points.back());
2207 bool trimmed = take_first ? cp1->next_trimmed : cp2->prev_trimmed;
2208 if (! trimmed) {
2209
2210 trimmed = cp1 == cp2 || polyline_idx1 == polyline_idx2 || (take_first ? cp2->consumed : cp1->consumed);
2211 if (! trimmed) {
2212 const bool cp1_first = ((cp1 - graph.map_infill_end_point_to_boundary.data()) & 1) == 0;
2213 const ContourIntersectionPoint* cp1_other = cp1_first ? cp1 + 1 : cp1 - 1;
2214
2215 trimmed = cp2 == cp1_other;
2216 }
2217 if (trimmed) {
2218
2219
2220
2222 if (take_first) {
2223 cp1->trim_next(std::max(0., len - trim_length -
SCALED_EPSILON));
2224 cp2->trim_prev(0);
2225 } else {
2226 cp1->trim_next(0);
2227 cp2->trim_prev(std::max(0., len - trim_length -
SCALED_EPSILON));
2228 }
2229 }
2230 }
2231 if (trimmed) {
2232 if (take_first)
2234 else
2236 } else if (! cp1->consumed && ! cp2->consumed) {
2238 polyline1.reverse();
2240 polyline2.reverse();
2241 take(polyline1, polyline2,
contour, cp1, cp2,
false);
2242
2243 if (polyline_idx2 < polyline_idx1) {
2244 polyline2 = std::move(polyline1);
2245 polyline1.points.clear();
2246 merged_with[polyline_idx1] = merged_with[polyline_idx2];
2247 } else {
2248 polyline2.points.clear();
2249 merged_with[polyline_idx2] = merged_with[polyline_idx1];
2250 }
2251 }
2252 };
2253
2254
2255
2256 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2258 if (cp.consumed)
2259 continue;
2260 const ContourIntersectionPoint &cp_other = graph.other(cp);
2261 assert((cp.next_on_contour == &cp_other) == (cp_other.prev_on_contour == &cp));
2262 assert((cp.prev_on_contour == &cp_other) == (cp_other.next_on_contour == &cp));
2265
2266 assert((cp.next_on_contour == &cp) == (cp.prev_on_contour == &cp));
2267 bool can_take_prev = vertical(dir_prev) && ! cp.prev_on_contour->consumed && cp.prev_on_contour != &cp_other;
2268 bool can_take_next = vertical(dir_next) && ! cp.next_on_contour->consumed && cp.next_on_contour != &cp_other;
2269 if (can_take_prev && (! can_take_next || take_vertical_prev(cp))) {
2270 if (! cp.prev_trimmed || cp.contour_not_taken_length_prev > min_arch_length)
2271
2272 take_next(*cp.prev_on_contour, false);
2273 } else if (can_take_next) {
2274 if (! cp.next_trimmed || cp.contour_not_taken_length_next > min_arch_length)
2275
2276 take_next(cp, true);
2277 }
2278 }
2279
2280#ifdef INFILL_DEBUG_OUTPUT
2281 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-vertical-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2282#endif
2283
2285 static const double cost_low = line_spacing * 1.3;
2286 static const double cost_high = line_spacing * 2.;
2287 static const double cost_veryhigh = line_spacing * 3.;
2288
2289 {
2290 std::vector<const SupportArcCost*> selected;
2291 selected.reserve(graph.map_infill_end_point_to_boundary.size());
2292 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2293 if (cp.consumed)
2294 continue;
2295 const SupportArcCost &cost_prev = arches[(&cp - graph.map_infill_end_point_to_boundary.data()) * 2];
2296 const SupportArcCost &cost_next = *(&cost_prev + 1);
2297 double cost_min = cost_prev.cost;
2298 double cost_max = cost_next.cost;
2299 if (cost_min > cost_max)
2300 std::swap(cost_min, cost_max);
2301 if (cost_max < cost_low || cost_min > cost_high)
2302
2303 continue;
2304 const double cost_diff_relative = (cost_max - cost_min) / cost_max;
2305 if (cost_diff_relative < 0.25)
2306
2307 continue;
2308 if (cost_prev.cost > cost_low)
2309 selected.emplace_back(&cost_prev);
2310 if (cost_next.cost > cost_low)
2311 selected.emplace_back(&cost_next);
2312 }
2313
2314 std::sort(selected.begin(), selected.end(), [](const auto *l, const auto *r) { return l->cost > r->cost; });
2315
2316 for (const SupportArcCost *arc : selected) {
2317 ContourIntersectionPoint &cp = graph.map_infill_end_point_to_boundary[(arc - arches.data()) / 2];
2318 if (! cp.consumed) {
2319 bool prev = ((arc - arches.data()) & 1) == 0;
2320 if (prev)
2321 take_next(*cp.prev_on_contour, false);
2322 else
2323 take_next(cp, true);
2324 }
2325 }
2326 }
2327
2328#if 0
2329 {
2330
2331
2332 struct Arc {
2334 double arc_length;
2335 bool take_next;
2336 };
2337 std::vector<Arc> arches;
2338 arches.reserve(graph.map_infill_end_point_to_boundary.size());
2339 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2340 if (cp.consumed)
2341 continue;
2342
2343 assert(cp.next_on_contour != &cp);
2344 const bool first = ((&cp - graph.map_infill_end_point_to_boundary.data()) & 1) == 0;
2345 const ContourIntersectionPoint *other_end = first ? &cp + 1 : &cp - 1;
2346 const bool loop_next = cp.next_on_contour == other_end;
2347 if (! loop_next && cp.could_connect_next()) {
2348 if (cp.contour_not_taken_length_next > min_arch_length) {
2349
2350 arches.push_back({ &cp, cp.contour_not_taken_length_next, true });
2351 arches.push_back({ cp.next_on_contour, cp.contour_not_taken_length_next, false });
2352 }
2353 } else {
2354
2355 if (cp.prev_trimmed && cp.could_take_prev()) {
2356
2357
2358 const Point &pt = graph.point(cp);
2359 const Point &prev = graph.point(*cp.prev_on_contour);
2360 if (std::abs(pt.x() - prev.x()) <
coord_t(0.5 * line_spacing)) {
2361
2362
2363 if (cp.contour_not_taken_length_prev > 0.5 * line_spacing)
2364 arches.push_back({ &cp, cp.contour_not_taken_length_prev, false });
2365 } else {
2366
2367 if (cp.contour_not_taken_length_prev > min_arch_length)
2368 arches.push_back({ &cp, cp.contour_not_taken_length_prev, false });
2369 }
2370 }
2371 if (cp.next_trimmed && cp.could_take_next()) {
2372
2373 const Point &pt = graph.point(cp);
2374 const Point &next = graph.point(*cp.next_on_contour);
2375 if (std::abs(pt.x() - next.x()) <
coord_t(0.5 * line_spacing)) {
2376
2377
2378 if (cp.contour_not_taken_length_next > 0.5 * line_spacing)
2379 arches.push_back({ &cp, cp.contour_not_taken_length_next, true });
2380 } else {
2381
2382 if (cp.contour_not_taken_length_next > min_arch_length)
2383 arches.push_back({ &cp, cp.contour_not_taken_length_next, true });
2384 }
2385 }
2386 }
2387 }
2388
2389 std::sort(arches.begin(), arches.end(), [](const auto &l, const auto &r) { return l.arc_length > r.arc_length; });
2390
2391 for (Arc &arc : arches)
2394 else
2395 take_next(*arc.intersection->prev_on_contour, false);
2396 }
2397#endif
2398
2399#ifdef INFILL_DEBUG_OUTPUT
2400 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-arches-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2401#endif
2402
2403
2404 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2406 if (cp.consumed)
2407 continue;
2408 bool first = ((&cp - graph.map_infill_end_point_to_boundary.data()) & 1) == 0;
2409 if (first) {
2410
2411 if (get_and_update_merged_with(&cp) != get_and_update_merged_with(cp.next_on_contour))
2412 take_next(cp, true);
2413 } else {
2414 if (get_and_update_merged_with(&cp) != get_and_update_merged_with(cp.prev_on_contour))
2415 take_next(*cp.prev_on_contour, false);
2416 }
2417 }
2418
2419#ifdef INFILL_DEBUG_OUTPUT
2420 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-zigzag-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2421#endif
2422
2423
2424 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2425 const bool first = ((&cp - graph.map_infill_end_point_to_boundary.data()) & 1) == 0;
2426 const ContourIntersectionPoint *other_end = first ? &cp + 1 : &cp - 1;
2427 const bool loop_next = cp.next_on_contour == other_end;
2428 const bool loop_prev = other_end->next_on_contour == &cp;
2429#ifndef NDEBUG
2430 const SupportArcCost &cost_prev = arches[(&cp - graph.map_infill_end_point_to_boundary.data()) * 2];
2431 const SupportArcCost &cost_next = *(&cost_prev + 1);
2432 assert(cost_prev.self_loop == loop_prev);
2433 assert(cost_next.self_loop == loop_next);
2434#endif
2435 if (loop_prev && cp.could_take_prev())
2436 take_next(*cp.prev_on_contour, false);
2437 if (loop_next && cp.could_take_next())
2438 take_next(cp, true);
2439 }
2440
2441#ifdef INFILL_DEBUG_OUTPUT
2442 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-caps-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2443#endif
2444
2445
2446 {
2447 std::vector<const SupportArcCost*> candidates;
2448 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2449 if (cp.could_take_prev())
2450 candidates.emplace_back(&arches[(&cp - graph.map_infill_end_point_to_boundary.data()) * 2]);
2451 if (cp.could_take_next())
2452 candidates.emplace_back(&arches[(&cp - graph.map_infill_end_point_to_boundary.data()) * 2 + 1]);
2453 }
2454 std::sort(candidates.begin(), candidates.end(), [](auto *c1, auto *c2) { return c1->cost > c2->cost; });
2455 for (const SupportArcCost *candidate : candidates) {
2456 ContourIntersectionPoint &cp = graph.map_infill_end_point_to_boundary[(candidate - arches.data()) / 2];
2457 bool prev = ((candidate - arches.data()) & 1) == 0;
2458 if (prev) {
2459 if (cp.could_take_prev() && (get_and_update_merged_with(&cp) != get_and_update_merged_with(cp.prev_on_contour) || candidate->cost > cost_high))
2460 take_next(*cp.prev_on_contour, false);
2461 } else {
2462 if (cp.could_take_next() && (get_and_update_merged_with(&cp) != get_and_update_merged_with(cp.next_on_contour) || candidate->cost > cost_high))
2463 take_next(cp, true);
2464 }
2465 }
2466 }
2467
2468#ifdef INFILL_DEBUG_OUTPUT
2469 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-Tjoints-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2470#endif
2471
2472
2473 const double cap_cost = 0.5 * line_spacing;
2474 for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
2475 const SupportArcCost &cost_prev = arches[(&cp - graph.map_infill_end_point_to_boundary.data()) * 2];
2476 const SupportArcCost &cost_next = *(&cost_prev + 1);
2478 (cost_prev.self_loop ?
2479 cost_prev.cost > cap_cost :
2480 cost_prev.cost > cost_veryhigh)) {
2481 assert(cp.consumed && (cp.prev_on_contour->consumed || cp.prev_trimmed));
2482 Polyline pl { graph.point(cp) };
2483 if (! cp.prev_trimmed) {
2484 cp.trim_prev(cp.contour_not_taken_length_prev - line_half_width);
2485 cp.prev_on_contour->trim_next(0);
2486 }
2488 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);
2489 cp.trim_prev(0);
2490 pl.clip_start(line_half_width);
2491 polylines_out.emplace_back(std::move(pl));
2492 }
2493 }
2495 (cost_next.self_loop ?
2496 cost_next.cost > cap_cost :
2497 cost_next.cost > cost_veryhigh)) {
2498 assert(cp.consumed && (cp.next_on_contour->consumed || cp.next_trimmed));
2499 Polyline pl { graph.point(cp) };
2500 if (! cp.next_trimmed) {
2501 cp.trim_next(cp.contour_not_taken_length_next - line_half_width);
2502 cp.next_on_contour->trim_prev(0);
2503 }
2505 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);
2506 cp.trim_next(0);
2507 pl.clip_start(line_half_width);
2508 polylines_out.emplace_back(std::move(pl));
2509 }
2510 }
2511 }
2512
2513#ifdef INFILL_DEBUG_OUTPUT
2514 export_partial_infill_to_svg(
debug_out_path(
"connect_base_support-final-%03d.svg", iRun), graph, infill_ordered, polylines_out);
2515#endif
2516
2517 polylines_out.reserve(polylines_out.size() + std::count_if(infill_ordered.begin(), infill_ordered.end(), [](const Polyline &pl) { return ! pl.empty(); }));
2518 for (Polyline &pl : infill_ordered)
2520 polylines_out.emplace_back(
std::move(pl));
2521}
Points points
Definition MultiPoint.hpp:18
if(!(yy_init))
Definition lexer.c:1190
std::string debug_out_path(const char *name,...)
Definition utils.cpp:218
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:343
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:686
static void take(Polyline &pl1, const Polyline &pl2, const Points &contour, size_t idx_start, size_t idx_end, bool clockwise)
Definition FillBase.cpp:384
double path_length_along_contour_ccw(const ContourIntersectionPoint *cp1, const ContourIntersectionPoint *cp2, double contour_length)
Definition FillBase.cpp:244
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:1776
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:1285
static bool cyclic_interval_inside_interval(double outer_low, double outer_high, double inner_low, double inner_high, double length)
Definition FillBase.cpp:710
static constexpr auto boundary_idx_unconnected
Definition FillBase.cpp:1116
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:430
static std::vector< SupportArcCost > evaluate_support_arches(Polylines &infill, BoundaryInfillGraph &graph, const double spacing, const FillParams ¶ms)
Definition FillBase.cpp:2024
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:287
static void mark_boundary_segments_overlapping_infill(BoundaryInfillGraph &graph, const Polylines &infill, const double spacing)
Definition FillBase.cpp:1205
const Polygon & contour(const ExPolygon &p)
Definition AGGRaster.hpp:21
static void base_support_extend_infill_lines(Polylines &infill, BoundaryInfillGraph &graph, const double spacing, const FillParams ¶ms)
Definition FillBase.cpp:1655
Direction
Definition FillBase.cpp:1151
@ Down
Definition FillBase.cpp:1155
@ Up
Definition FillBase.cpp:1154