diff --git a/resources/ui_layout/print.ui b/resources/ui_layout/print.ui index 9ae48f862..6e23f4848 100644 --- a/resources/ui_layout/print.ui +++ b/resources/ui_layout/print.ui @@ -41,12 +41,12 @@ group:Quality end_line group:label_width$12:Overhangs line:threshold for - setting:label$Bridge speed and fan:width$5:overhangs_width_speed - setting:label$Bridge flow:width$5:overhangs_width + setting:label$Bridge speed and fan:width$5:sidetext_width$0:overhangs_width_speed + setting:label_width$12:label$Bridge flow:width$5:overhangs_width end_line line:Extrusion direction - setting:sidetext_width$1:overhangs_reverse - setting:width$5:overhangs_reverse_threshold + setting:sidetext_width$2:overhangs_reverse + setting:label_width$12:width$5:overhangs_reverse_threshold end_line group:Advanced setting:width$25:no_perimeter_unsupported_algo @@ -55,13 +55,13 @@ group:Advanced setting:width$5:gap_fill_min_area end_line line:Seam - setting:sidetext_width$2:seam_position - setting:label_width$10:width$3:sidetext_width$2:seam_angle_cost - setting:label_width$10:width$3:sidetext_width$2:seam_travel_cost + setting:label_width$12:sidetext_width$0:seam_position + setting:width$3:sidetext_width$0:seam_angle_cost + setting:width$3:sidetext_width$0:seam_travel_cost end_line line:One-loop perimeters - setting:perimeter_loop - setting:perimeter_loop_seam + setting:sidetext_width$0:perimeter_loop + setting:label_width$5:label$Seam:perimeter_loop_seam end_line group: External perimeter first setting:label$Activate:external_perimeters_first @@ -102,28 +102,31 @@ group:Other setting:allow_empty_layers page:Infill:infill -group:Infill - line:Fill density - setting:label_width$5:label$_:fill_density - setting:width$19:label$_:infill_connection - setting:infill_anchor +group:title_width$8:Infill + line:Sparse + setting:width$5:label$_:sidetext_width$1:fill_density + setting:label_width$1:label$_:fill_pattern + setting:label$_:width$18:infill_connection + setting:sidetext_width$7:infill_anchor end_line - line:Pattern - setting:label_width$5:label$_:fill_pattern + line:Solid + setting:label_width$11:label$_:solid_fill_pattern + setting:label$_:width$18:infill_connection_solid end_line - line:External patterns - setting:label_width$5:top_fill_pattern - setting:label_width$5:bottom_fill_pattern + line:Top + setting:label_width$11:label$_:top_fill_pattern + setting:label$_:width$18:infill_connection_top end_line - line:Solid pattern - setting:label_width$5:label$_:solid_fill_pattern + line:Bottom + setting:label_width$11:label$_:bottom_fill_pattern + setting:label$_:width$18:infill_connection_bottom end_line group:Reducing printing time setting:infill_every_layers setting:infill_only_where_needed line:Supporting dense layer setting:sidetext_width$0:infill_dense - setting:infill_dense_algo + setting:width$20:infill_dense_algo end_line group:sidetext_width$3:Advanced setting:solid_infill_every_layers @@ -144,13 +147,13 @@ group:Advanced Infill options setting:label_width$8:width$5:fill_smooth_distribution setting:label_width$26:label$Spacing between ironing lines:width$5:sidetext_width$7:fill_smooth_width end_line -group:Ironing post-process (This will go on top of infills and perimeters) +group:title_width$19:Ironing post-process (This will go on top of infills and perimeters) line:Enable ironing post-process setting:label$_:sidetext_width$0:ironing setting:label$On:ironing_type end_line line:Tuning ironing - setting:label_width$8:width$5:ironing_flowrate + setting:label_width$9:width$5:ironing_flowrate setting:label_width$26:width$5:ironing_spacing end_line @@ -183,7 +186,7 @@ group:Raft setting:raft_layers group:Options for support material and raft line:Z-offset - setting:width$12:support_material_contact_distance_type + setting:width$11:support_material_contact_distance_type setting:width$6:support_material_contact_distance_top setting:width$6:support_material_contact_distance_bottom end_line diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 4eec5adcc..81a9f5d5e 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -115,16 +115,25 @@ std::vector group_fills(const Layer &layer) params.pattern = region_config.fill_pattern.value; params.density = float(region_config.fill_density) / 100.f; params.dont_adjust = false; + params.connection = region_config.infill_connection.value; if (surface.has_fill_solid()) { params.density = 1.f; params.pattern = ipRectilinear; + params.connection = region_config.infill_connection_solid.value; + if (surface.has_pos_top()) + params.connection = region_config.infill_connection_top.value; + if (surface.has_pos_bottom()) + params.connection = region_config.infill_connection_bottom.value; + if (is_bridge) + params.connection = InfillConnection::icConnected; if (surface.has_pos_external() && !is_bridge) params.pattern = surface.has_pos_top() ? region_config.top_fill_pattern.value : region_config.bottom_fill_pattern.value; else if (!is_bridge) params.pattern = region_config.solid_fill_pattern.value; } else { - + if (is_bridge) + params.connection = InfillConnection::icConnected; if (region_config.infill_dense.getBool() && region_config.fill_density < 40 && surface.maxNbSolidLayersOnTop == 1) { @@ -132,6 +141,7 @@ std::vector group_fills(const Layer &layer) is_denser = true; is_bridge = true; params.pattern = ipRectiWithPerimeter; + params.connection = InfillConnection::icConnected; } if (params.density <= 0) continue; @@ -156,7 +166,6 @@ std::vector group_fills(const Layer &layer) } } params.fill_exactly = region_config.enforce_full_fill_volume.getBool(); - params.connection = region_config.infill_connection.value; params.bridge_angle = float(surface.bridge_angle); if (is_denser) { params.angle = 0; @@ -186,7 +195,8 @@ std::vector group_fills(const Layer &layer) if (surface.has_fill_solid() || is_bridge) { params.spacing = params.flow.spacing(); // Don't limit anchor length for solid or bridging infill. - params.anchor_length = 1000.f; + // use old algo to prevent possible weird stuff from sparse bridging + params.anchor_length = is_bridge?0:1000.f; } else { // it's internal infill, so we can calculate a generic flow spacing // for all layers, for avoiding the ugly effect of @@ -204,8 +214,6 @@ std::vector group_fills(const Layer &layer) params.anchor_length = float(region_config.infill_anchor); if (region_config.infill_anchor.percent) params.anchor_length *= 0.01 * params.spacing; - // Don't limit anchor length for solid or bridging infill. - //FIXEME: totest params.anchor_length = 1000.f; } auto it_params = set_surface_params.find(params); diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index e35750f1a..4aa392f19 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -212,12 +212,66 @@ coord_t Fill::_line_spacing_for_density(float density) const return coord_t(scale_(this->get_spacing()) / density); } +//FIXME: add recent improvmeent from perimetergenerator: avoid thick gapfill +void +Fill::do_gap_fill(const ExPolygons& gapfill_areas, const FillParams& params, ExtrusionEntitiesPtr& coll_out) const { + + ThickPolylines polylines_gapfill; + double min = 0.4 * scale_(params.flow.nozzle_diameter) * (1 - INSET_OVERLAP_TOLERANCE); + double max = 2. * params.flow.scaled_width(); + // collapse + //be sure we don't gapfill where the perimeters are already touching each other (negative spacing). + min = std::max(min, double(Flow::new_from_spacing((float)EPSILON, (float)params.flow.nozzle_diameter, (float)params.flow.height, false).scaled_width())); + //ExPolygons gapfill_areas_collapsed = diff_ex( + // offset2_ex(gapfill_areas, double(-min / 2), double(+min / 2)), + // offset2_ex(gapfill_areas, double(-max / 2), double(+max / 2)), + // true); + ExPolygons gapfill_areas_collapsed = offset2_ex(gapfill_areas, double(-min / 2), double(+min / 2)); + double minarea = params.flow.scaled_width() * params.flow.scaled_width(); + if (params.config != nullptr) minarea = scale_(params.config->gap_fill_min_area.get_abs_value(params.flow.width)) * params.flow.scaled_width(); + for (const ExPolygon& ex : gapfill_areas_collapsed) { + //remove too small gaps that are too hard to fill. + //ie one that are smaller than an extrusion with width of min and a length of max. + if (ex.area() > minarea) { + MedialAxis{ ex, params.flow.scaled_width() * 2, params.flow.scaled_width() / 5, coord_t(params.flow.height) }.build(polylines_gapfill); + } + } + if (!polylines_gapfill.empty() && params.role != erBridgeInfill && params.role != erInternalBridgeInfill) { + //test +#ifdef _DEBUG + for (ThickPolyline poly : polylines_gapfill) { + for (coordf_t width : poly.width) { + if (width > params.flow.scaled_width() * 2.2) { + std::cerr << "ERRROR!!!! gapfill width = " << unscaled(width) << " > max_width = " << (params.flow.width * 2) << "\n"; + } + } + } +#endif + + ExtrusionEntityCollection gap_fill = thin_variable_width(polylines_gapfill, erGapFill, params.flow); + //set role if needed + /*if (params.role != erSolidInfill) { + ExtrusionSetRole set_good_role(params.role); + gap_fill.visit(set_good_role); + }*/ + //move them into the collection + if (!gap_fill.entities.empty()) { + ExtrusionEntityCollection* coll_gapfill = new ExtrusionEntityCollection(); + coll_gapfill->no_sort = this->no_sort(); + coll_gapfill->append(std::move(gap_fill.entities)); + coll_out.push_back(coll_gapfill); + } + } +} + +namespace NaiveConnect { + /// cut poly between poly.point[idx_1] & poly.point[idx_1+1] /// add p1+-width to one part and p2+-width to the other one. /// add the "new" polyline to polylines (to part cut from poly) /// p1 & p2 have to be between poly.point[idx_1] & poly.point[idx_1+1] /// if idx_1 is ==0 or == size-1, then we don't need to create a new polyline. -void cut_polyline(Polyline &poly, Polylines &polylines, size_t idx_1, Point p1, Point p2) { +void cut_polyline(Polyline& poly, Polylines& polylines, size_t idx_1, Point p1, Point p2) { //reorder points if (p1.distance_to_square(poly.points[idx_1]) > p2.distance_to_square(poly.points[idx_1])) { Point temp = p2; @@ -245,7 +299,7 @@ void cut_polyline(Polyline &poly, Polylines &polylines, size_t idx_1, Point p1, } /// the poly is like a polygon but with first_point != last_point (already removed) -void cut_polygon(Polyline &poly, size_t idx_1, Point p1, Point p2) { +void cut_polygon(Polyline& poly, size_t idx_1, Point p1, Point p2) { //reorder points if (p1.distance_to_square(poly.points[idx_1]) > p2.distance_to_square(poly.points[idx_1])) { Point temp = p2; @@ -267,15 +321,15 @@ void cut_polygon(Polyline &poly, size_t idx_1, Point p1, Point p2) { /// it use equally_spaced_points with width/2 precision, so don't worry with pts_to_check number of points. /// it use the given polylines_blocker points, be sure to put enough of them to be reliable. /// complexity : N(pts_to_check.equally_spaced_points(width / 2)) x N(polylines_blocker.points) -bool collision(const Points &pts_to_check, const Polylines &polylines_blocker, const coord_t width) { +bool collision(const Points& pts_to_check, const Polylines& polylines_blocker, const coord_t width) { //check if it's not too close to a polyline //convert to double to allow ² operation double min_dist_square = (double)width * (double)width * 0.9 - SCALED_EPSILON; Polyline better_polylines(pts_to_check); Points better_pts = better_polylines.equally_spaced_points(double(width / 2)); - for (const Point &p : better_pts) { - for (const Polyline &poly2 : polylines_blocker) { - for (const Point &p2 : poly2.points) { + for (const Point& p : better_pts) { + for (const Polyline& poly2 : polylines_blocker) { + for (const Point& p2 : poly2.points) { if (p.distance_to_square(p2) < min_dist_square) { return true; } @@ -291,9 +345,9 @@ bool collision(const Points &pts_to_check, const Polylines &polylines_blocker, c /// complexity: N(polylines.points) + a collision check after that if we finded a path: N(2(p2-p1)/width) x N(polylines_blocker.points) /// @param width is scaled /// @param max_size is scaled -Points getFrontier(Polylines &polylines, const Point& p1, const Point& p2, const coord_t width, const Polylines &polylines_blockers, coord_t max_size = -1) { +Points getFrontier(Polylines& polylines, const Point& p1, const Point& p2, const coord_t width, const Polylines& polylines_blockers, coord_t max_size = -1) { for (size_t idx_poly = 0; idx_poly < polylines.size(); ++idx_poly) { - Polyline &poly = polylines[idx_poly]; + Polyline& poly = polylines[idx_poly]; if (poly.size() <= 1) continue; //loop? @@ -333,7 +387,7 @@ Points getFrontier(Polylines &polylines, const Point& p1, const Point& p2, const Points ret_1_to_2; double dist_1_to_2 = p1.distance_to(poly.points[idx_12]); ret_1_to_2.push_back(poly.points[idx_12]); - size_t max = idx_12 <= idx_21 ? idx_21+1 : poly.points.size(); + size_t max = idx_12 <= idx_21 ? idx_21 + 1 : poly.points.size(); for (size_t i = idx_12 + 1; i < max; i++) { dist_1_to_2 += poly.points[i - 1].distance_to(poly.points[i]); ret_1_to_2.push_back(poly.points[i]); @@ -469,15 +523,14 @@ Points getFrontier(Polylines &polylines, const Point& p1, const Point& p2, const /// return the connected polylines in polylines_out. Can output polygons (stored as polylines with first_point = last_point). /// complexity: worst: N(infill_ordered.points) x N(boundary.points) /// typical: N(infill_ordered) x ( N(boundary.points) + N(infill_ordered.points) ) -void -Fill::connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const FillParams ¶ms) const { +void connect_infill(const Polylines& infill_ordered, const ExPolygon& boundary, Polylines& polylines_out, const double spacing, const FillParams& params) { //TODO: fallback to the quick & dirty old algorithm when n(points) is too high. Polylines polylines_frontier = to_polylines(((Polygons)boundary)); Polylines polylines_blocker; - coord_t clip_size = scale_(this->get_spacing()) * 2; - for (const Polyline &polyline : infill_ordered) { + coord_t clip_size = scale_(spacing) * 2; + for (const Polyline& polyline : infill_ordered) { if (polyline.length() > 2.01 * clip_size) { polylines_blocker.push_back(polyline); polylines_blocker.back().clip_end((double)clip_size); @@ -486,18 +539,18 @@ Fill::connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, } //length between two lines - coordf_t ideal_length = (1 / params.density) * this->get_spacing(); + coordf_t ideal_length = (1 / params.density) * spacing; Polylines polylines_connected_first; bool first = true; - for (const Polyline &polyline : infill_ordered) { + for (const Polyline& polyline : infill_ordered) { if (!first) { // Try to connect the lines. - Points &pts_end = polylines_connected_first.back().points; - const Point &last_point = pts_end.back(); - const Point &first_point = polyline.points.front(); - if (last_point.distance_to(first_point) < scale_(this->get_spacing()) * 10) { - Points pts_frontier = getFrontier(polylines_frontier, last_point, first_point, scale_(this->get_spacing()), polylines_blocker, scale_(ideal_length) * 2); + Points& pts_end = polylines_connected_first.back().points; + const Point& last_point = pts_end.back(); + const Point& first_point = polyline.points.front(); + if (last_point.distance_to(first_point) < scale_(spacing) * 10) { + Points pts_frontier = getFrontier(polylines_frontier, last_point, first_point, scale_(spacing), polylines_blocker, scale_(ideal_length) * 2); if (!pts_frontier.empty()) { // The lines can be connected. pts_end.insert(pts_end.end(), pts_frontier.begin(), pts_frontier.end()); @@ -514,15 +567,15 @@ Fill::connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, Polylines polylines_connected; first = true; - for (const Polyline &polyline : polylines_connected_first) { + for (const Polyline& polyline : polylines_connected_first) { if (!first) { // Try to connect the lines. - Points &pts_end = polylines_connected.back().points; - const Point &last_point = pts_end.back(); - const Point &first_point = polyline.points.front(); + Points& pts_end = polylines_connected.back().points; + const Point& last_point = pts_end.back(); + const Point& first_point = polyline.points.front(); Polylines before = polylines_frontier; - Points pts_frontier = getFrontier(polylines_frontier, last_point, first_point, scale_(this->get_spacing()), polylines_blocker); + Points pts_frontier = getFrontier(polylines_frontier, last_point, first_point, scale_(spacing), polylines_blocker); if (!pts_frontier.empty()) { // The lines can be connected. pts_end.insert(pts_end.end(), pts_frontier.begin(), pts_frontier.end()); @@ -555,15 +608,15 @@ Fill::connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, min_length = min; } } - if (min_idx > idx1 && min_idx < polylines_connected.size()){ - Points pts_frontier = getFrontier(polylines_frontier, - switch_id1 ? polylines_connected[idx1].first_point() : polylines_connected[idx1].last_point(), + if (min_idx > idx1&& min_idx < polylines_connected.size()) { + Points pts_frontier = getFrontier(polylines_frontier, + switch_id1 ? polylines_connected[idx1].first_point() : polylines_connected[idx1].last_point(), switch_id2 ? polylines_connected[min_idx].last_point() : polylines_connected[min_idx].first_point(), - scale_(this->get_spacing()), polylines_blocker); + scale_(spacing), polylines_blocker); if (!pts_frontier.empty()) { if (switch_id1) polylines_connected[idx1].reverse(); if (switch_id2) polylines_connected[min_idx].reverse(); - Points &pts_end = polylines_connected[idx1].points; + Points& pts_end = polylines_connected[idx1].points; pts_end.insert(pts_end.end(), pts_frontier.begin(), pts_frontier.end()); pts_end.insert(pts_end.end(), polylines_connected[min_idx].points.begin(), polylines_connected[min_idx].points.end()); polylines_connected.erase(polylines_connected.begin() + min_idx); @@ -572,8 +625,8 @@ Fill::connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, } //try to create some loops if possible - for (Polyline &polyline : polylines_connected) { - Points pts_frontier = getFrontier(polylines_frontier, polyline.last_point(), polyline.first_point(), scale_(this->get_spacing()), polylines_blocker); + for (Polyline& polyline : polylines_connected) { + Points pts_frontier = getFrontier(polylines_frontier, polyline.last_point(), polyline.first_point(), scale_(spacing), polylines_blocker); if (!pts_frontier.empty()) { polyline.points.insert(polyline.points.end(), pts_frontier.begin(), pts_frontier.end()); polyline.points.insert(polyline.points.begin(), polyline.points.back()); @@ -582,59 +635,513 @@ Fill::connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, } } +} +namespace PrusaSimpleConnect { -void -Fill::do_gap_fill(const ExPolygons &gapfill_areas, const FillParams ¶ms, ExtrusionEntitiesPtr &coll_out) const { + struct ContourPointData { + ContourPointData(float param) : param(param) {} + // Eucleidean position of the contour point along the contour. + float param = 0.f; + // Was the segment starting with this contour point extruded? + bool segment_consumed = false; + // Was this point extruded over? + bool point_consumed = false; + }; - ThickPolylines polylines_gapfill; - double min = 0.4 * scale_(params.flow.nozzle_diameter) * (1 - INSET_OVERLAP_TOLERANCE); - double max = 2. * params.flow.scaled_width(); - // collapse - //be sure we don't gapfill where the perimeters are already touching each other (negative spacing). - min = std::max(min, double(Flow::new_from_spacing((float)EPSILON, (float)params.flow.nozzle_diameter, (float)params.flow.height, false).scaled_width())); - //ExPolygons gapfill_areas_collapsed = diff_ex( - // offset2_ex(gapfill_areas, double(-min / 2), double(+min / 2)), - // offset2_ex(gapfill_areas, double(-max / 2), double(+max / 2)), - // true); - ExPolygons gapfill_areas_collapsed = offset2_ex(gapfill_areas, double(-min / 2), double(+min / 2)); - double minarea = params.flow.scaled_width() * params.flow.scaled_width(); - if (params.config != nullptr) minarea = scale_(params.config->gap_fill_min_area.get_abs_value(params.flow.width)) * params.flow.scaled_width(); - for (const ExPolygon &ex : gapfill_areas_collapsed) { - //remove too small gaps that are too hard to fill. - //ie one that are smaller than an extrusion with width of min and a length of max. - if (ex.area() > minarea) { - MedialAxis{ ex, params.flow.scaled_width() * 2, params.flow.scaled_width() / 5, coord_t(params.flow.height) }.build(polylines_gapfill); + // Verify whether the contour from point idx_start to point idx_end could be taken (whether all segments along the contour were not yet extruded). + static bool could_take(const std::vector& contour_data, size_t idx_start, size_t idx_end) + { + assert(idx_start != idx_end); + for (size_t i = idx_start; i != idx_end; ) { + if (contour_data[i].segment_consumed || contour_data[i].point_consumed) + return false; + if (++i == contour_data.size()) + i = 0; } + return !contour_data[idx_end].point_consumed; } - if (!polylines_gapfill.empty() && params.role != erBridgeInfill && params.role != erInternalBridgeInfill) { - //test -#ifdef _DEBUG - for (ThickPolyline poly : polylines_gapfill) { - for (coordf_t width : poly.width) { - if (width > params.flow.scaled_width() * 2.2) { - std::cerr << "ERRROR!!!! gapfill width = " << unscaled(width) << " > max_width = " << (params.flow.width * 2) << "\n"; + + // Connect end of pl1 to the start of pl2 using the perimeter contour. + // The idx_start and idx_end are ordered so that the connecting polyline points will be taken with increasing indices. + static void take(Polyline& pl1, Polyline&& pl2, const Points& contour, std::vector& contour_data, size_t idx_start, size_t idx_end, bool reversed) + { +#ifndef NDEBUG + size_t num_points_initial = pl1.points.size(); + assert(idx_start != idx_end); +#endif /* NDEBUG */ + + { + // Reserve memory at pl1 for the connecting contour and pl2. + int new_points = int(idx_end) - int(idx_start) - 1; + if (new_points < 0) + new_points += int(contour.size()); + pl1.points.reserve(pl1.points.size() + size_t(new_points) + pl2.points.size()); + } + + contour_data[idx_start].point_consumed = true; + contour_data[idx_start].segment_consumed = true; + contour_data[idx_end].point_consumed = true; + + if (reversed) { + size_t i = (idx_end == 0) ? contour_data.size() - 1 : idx_end - 1; + while (i != idx_start) { + contour_data[i].point_consumed = true; + contour_data[i].segment_consumed = true; + pl1.points.emplace_back(contour[i]); + if (i == 0) + i = contour_data.size(); + --i; + } + } else { + size_t i = idx_start; + if (++i == contour_data.size()) + i = 0; + while (i != idx_end) { + contour_data[i].point_consumed = true; + contour_data[i].segment_consumed = true; + pl1.points.emplace_back(contour[i]); + if (++i == contour_data.size()) + i = 0; + } + } + + append(pl1.points, std::move(pl2.points)); + } + + // Return an index of start of a segment and a point of the clipping point at distance from the end of polyline. + struct SegmentPoint { + // Segment index, defining a line ::max(); + // Parameter of point in <0, 1) along the line ::max(); } + }; + + static inline SegmentPoint clip_start_segment_and_point(const Points& polyline, double distance) + { + assert(polyline.size() >= 2); + assert(distance > 0.); + // Initialized to "invalid". + SegmentPoint out; + if (polyline.size() >= 2) { + Vec2d pt_prev = polyline.front().cast(); + for (size_t i = 1; i < polyline.size(); ++i) { + Vec2d pt = polyline[i].cast(); + Vec2d v = pt - pt_prev; + double l2 = v.squaredNorm(); + if (l2 > distance* distance) { + out.idx_segment = i; + out.t = distance / sqrt(l2); + out.point = pt_prev + out.t * v; + break; + } + distance -= sqrt(l2); + pt_prev = pt; + } + } + return out; + } + + static inline SegmentPoint clip_end_segment_and_point(const Points& polyline, double distance) + { + assert(polyline.size() >= 2); + assert(distance > 0.); + // Initialized to "invalid". + SegmentPoint out; + if (polyline.size() >= 2) { + Vec2d pt_next = polyline.back().cast(); + for (int i = int(polyline.size()) - 2; i >= 0; --i) { + Vec2d pt = polyline[i].cast(); + Vec2d v = pt - pt_next; + double l2 = v.squaredNorm(); + if (l2 > distance* distance) { + out.idx_segment = i; + out.t = distance / sqrt(l2); + out.point = pt_next + out.t * v; + // Store the parameter referenced to the starting point of a segment. + out.t = 1. - out.t; + break; + } + distance -= sqrt(l2); + pt_next = pt; + } + } + return out; + } + + // Optimized version with the precalculated v1 = p1b - p1a and l1_2 = v1.squaredNorm(). + // Assumption: l1_2 < EPSILON. + static inline double segment_point_distance_squared(const Vec2d& p1a, const Vec2d& p1b, const Vec2d& v1, const double l1_2, const Vec2d& p2) + { + assert(l1_2 > EPSILON); + Vec2d v12 = p2 - p1a; + double t = v12.dot(v1); + return (t <= 0.) ? v12.squaredNorm() : + (t >= l1_2) ? (p2 - p1a).squaredNorm() : + ((t / l1_2) * v1 - v12).squaredNorm(); + } + + static inline double segment_point_distance_squared(const Vec2d& p1a, const Vec2d& p1b, const Vec2d& p2) + { + const Vec2d v = p1b - p1a; + const double l2 = v.squaredNorm(); + if (l2 < EPSILON) + // p1a == p1b + return (p2 - p1a).squaredNorm(); + return segment_point_distance_squared(p1a, p1b, v, v.squaredNorm(), p2); + } + + // Distance to the closest point of line. + static inline double min_distance_of_segments(const Vec2d& p1a, const Vec2d& p1b, const Vec2d& p2a, const Vec2d& p2b) + { + Vec2d v1 = p1b - p1a; + double l1_2 = v1.squaredNorm(); + if (l1_2 < EPSILON) + // p1a == p1b: Return distance of p1a from the (p2a, p2b) segment. + return segment_point_distance_squared(p2a, p2b, p1a); + + Vec2d v2 = p2b - p2a; + double l2_2 = v2.squaredNorm(); + if (l2_2 < EPSILON) + // p2a == p2b: Return distance of p2a from the (p1a, p1b) segment. + return segment_point_distance_squared(p1a, p1b, v1, l1_2, p2a); + + return std::min( + std::min(segment_point_distance_squared(p1a, p1b, v1, l1_2, p2a), segment_point_distance_squared(p1a, p1b, v1, l1_2, p2b)), + std::min(segment_point_distance_squared(p2a, p2b, v2, l2_2, p1a), segment_point_distance_squared(p2a, p2b, v2, l2_2, p1b))); + } + + // Mark the segments of split boundary as consumed if they are very close to some of the infill line. + void mark_boundary_segments_touching_infill( + const std::vector& boundary, + std::vector>& boundary_data, + const BoundingBox& boundary_bbox, + const Polylines& infill, + const double clip_distance, + const double distance_colliding) + { + EdgeGrid::Grid grid; + grid.set_bbox(boundary_bbox.inflated(distance_colliding * 1.43)); + // Inflate the bounding box by a thick line width. + grid.create(boundary, coord_t(clip_distance + scale_(10.))); + + struct Visitor { + Visitor(const EdgeGrid::Grid& grid, const std::vector& boundary, std::vector>& boundary_data, const double dist2_max) : + grid(grid), boundary(boundary), boundary_data(boundary_data), dist2_max(dist2_max) {} + + void init(const Vec2d& pt1, const Vec2d& pt2) { + this->pt1 = &pt1; + this->pt2 = &pt2; + } + + bool operator()(coord_t iy, coord_t ix) { + // Called with a row and colum of the grid cell, which is intersected by a line. + auto cell_data_range = this->grid.cell_data_range(iy, ix); + for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++it_contour_and_segment) { + // End points of the line segment and their vector. + auto segment = this->grid.segment(*it_contour_and_segment); + const Vec2d seg_pt1 = segment.first.cast(); + const Vec2d seg_pt2 = segment.second.cast(); + if (min_distance_of_segments(seg_pt1, seg_pt2, *this->pt1, *this->pt2) < this->dist2_max) { + // Mark this boundary segment as touching the infill line. + ContourPointData& bdp = boundary_data[it_contour_and_segment->first][it_contour_and_segment->second]; + bdp.segment_consumed = true; + // There is no need for checking seg_pt2 as it will be checked the next time. + bool point_touching = false; + if (segment_point_distance_squared(*this->pt1, *this->pt2, seg_pt1) < this->dist2_max) { + point_touching = true; + bdp.point_consumed = true; + } +#if 0 + { + static size_t iRun = 0; + ExPolygon expoly(Polygon(*grid.contours().front())); + for (size_t i = 1; i < grid.contours().size(); ++i) + expoly.holes.emplace_back(Polygon(*grid.contours()[i])); + SVG svg(debug_out_path("%s-%d.svg", "FillBase-mark_boundary_segments_touching_infill", iRun++).c_str(), get_extents(expoly)); + svg.draw(expoly, "green"); + svg.draw(Line(segment.first, segment.second), "red"); + svg.draw(Line(this->pt1->cast(), this->pt2->cast()), "magenta"); + } +#endif + } + } + // Continue traversing the grid along the edge. + return true; + } + + const EdgeGrid::Grid& grid; + const std::vector& boundary; + std::vector>& boundary_data; + // Maximum distance between the boundary and the infill line allowed to consider the boundary not touching the infill line. + const double dist2_max; + + const Vec2d* pt1; + const Vec2d* pt2; + } visitor(grid, boundary, boundary_data, distance_colliding * distance_colliding); + + BoundingBoxf bboxf(boundary_bbox.min.cast(), boundary_bbox.max.cast()); + bboxf.offset(coordf_t(-SCALED_EPSILON)); + + for (const Polyline& polyline : infill) { + // Clip the infill polyline by the Eucledian distance along the polyline. + SegmentPoint start_point = clip_start_segment_and_point(polyline.points, clip_distance); + SegmentPoint end_point = clip_end_segment_and_point(polyline.points, clip_distance); + if (start_point.valid() && end_point.valid() && + (start_point.idx_segment < end_point.idx_segment || (start_point.idx_segment == end_point.idx_segment && start_point.t < end_point.t))) { + // The clipped polyline is non-empty. + for (size_t point_idx = start_point.idx_segment; point_idx <= end_point.idx_segment; ++point_idx) { + //FIXME extend the EdgeGrid to suport tracing a thick line. +#if 0 + Point pt1, pt2; + Vec2d pt1d, pt2d; + if (point_idx == start_point.idx_segment) { + pt1d = start_point.point; + pt1 = pt1d.cast(); + } else { + pt1 = polyline.points[point_idx]; + pt1d = pt1.cast(); + } + if (point_idx == start_point.idx_segment) { + pt2d = end_point.point; + pt2 = pt1d.cast(); + } else { + pt2 = polyline.points[point_idx]; + pt2d = pt2.cast(); + } + visitor.init(pt1d, pt2d); + grid.visit_cells_intersecting_thick_line(pt1, pt2, distance_colliding, visitor); +#else + Vec2d pt1 = (point_idx == start_point.idx_segment) ? start_point.point : polyline.points[point_idx].cast(); + Vec2d pt2 = (point_idx == end_point.idx_segment) ? end_point.point : polyline.points[point_idx + 1].cast(); +#if 0 + { + static size_t iRun = 0; + ExPolygon expoly(Polygon(*grid.contours().front())); + for (size_t i = 1; i < grid.contours().size(); ++i) + expoly.holes.emplace_back(Polygon(*grid.contours()[i])); + SVG svg(debug_out_path("%s-%d.svg", "FillBase-mark_boundary_segments_touching_infill0", iRun++).c_str(), get_extents(expoly)); + svg.draw(expoly, "green"); + svg.draw(polyline, "blue"); + svg.draw(Line(pt1.cast(), pt2.cast()), "magenta", scale_(0.1)); + } +#endif + visitor.init(pt1, pt2); + // Simulate tracing of a thick line. This only works reliably if distance_colliding <= grid cell size. + Vec2d v = (pt2 - pt1).normalized() * distance_colliding; + Vec2d vperp(-v.y(), v.x()); + Vec2d a = pt1 - v - vperp; + Vec2d b = pt1 + v - vperp; + if (Geometry::liang_barsky_line_clipping(a, b, bboxf)) + grid.visit_cells_intersecting_line(a.cast(), b.cast(), visitor); + a = pt1 - v + vperp; + b = pt1 + v + vperp; + if (Geometry::liang_barsky_line_clipping(a, b, bboxf)) + grid.visit_cells_intersecting_line(a.cast(), b.cast(), visitor); +#endif } } } -#endif + } - ExtrusionEntityCollection gap_fill = thin_variable_width(polylines_gapfill, erGapFill, params.flow); - //set role if needed - /*if (params.role != erSolidInfill) { - ExtrusionSetRole set_good_role(params.role); - gap_fill.visit(set_good_role); - }*/ - //move them into the collection - if (!gap_fill.entities.empty()) { - ExtrusionEntityCollection *coll_gapfill = new ExtrusionEntityCollection(); - coll_gapfill->no_sort = this->no_sort(); - coll_gapfill->append(std::move(gap_fill.entities)); - coll_out.push_back(coll_gapfill); + void connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary_src, Polylines &polylines_out, const double spacing, const FillParams ¶ms) + { + assert(!infill_ordered.empty()); + assert(!boundary_src.contour.points.empty()); + + BoundingBox bbox = get_extents(boundary_src.contour); + bbox.offset(coordf_t(SCALED_EPSILON)); + + // 1) Add the end points of infill_ordered to boundary_src. + std::vector boundary; + std::vector> boundary_data; + boundary.assign(boundary_src.holes.size() + 1, Points()); + boundary_data.assign(boundary_src.holes.size() + 1, std::vector()); + // Mapping the infill_ordered end point to a (contour, point) of boundary. + std::vector> map_infill_end_point_to_boundary; + static constexpr auto boundary_idx_unconnected = std::numeric_limits::max(); + map_infill_end_point_to_boundary.assign(infill_ordered.size() * 2, std::pair(boundary_idx_unconnected, boundary_idx_unconnected)); + { + // Project the infill_ordered end points onto boundary_src. + std::vector> intersection_points; + { + EdgeGrid::Grid grid; + grid.set_bbox(bbox); + grid.create(boundary_src, scale_(10.)); + intersection_points.reserve(infill_ordered.size() * 2); + for (const Polyline& pl : infill_ordered) + for (const Point* pt : { &pl.points.front(), &pl.points.back() }) { + EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point(*pt, SCALED_EPSILON); + if (cp.valid()) { + // The infill end point shall lie on the contour. + assert(cp.distance < 2.); + intersection_points.emplace_back(cp, (&pl - infill_ordered.data()) * 2 + (pt == &pl.points.front() ? 0 : 1)); + } + } + std::sort(intersection_points.begin(), intersection_points.end(), [](const std::pair& cp1, const std::pair& cp2) { + return cp1.first.contour_idx < cp2.first.contour_idx || + (cp1.first.contour_idx == cp2.first.contour_idx && + (cp1.first.start_point_idx < cp2.first.start_point_idx || + (cp1.first.start_point_idx == cp2.first.start_point_idx && cp1.first.t < cp2.first.t))); + }); + } + auto it = intersection_points.begin(); + auto it_end = intersection_points.end(); + for (size_t idx_contour = 0; idx_contour <= boundary_src.holes.size(); ++idx_contour) { + const Polygon& contour_src = (idx_contour == 0) ? boundary_src.contour : boundary_src.holes[idx_contour - 1]; + Points& contour_dst = boundary[idx_contour]; + for (size_t idx_point = 0; idx_point < contour_src.points.size(); ++idx_point) { + contour_dst.emplace_back(contour_src.points[idx_point]); + for (; it != it_end && it->first.contour_idx == idx_contour && it->first.start_point_idx == idx_point; ++it) { + // Add these points to the destination contour. + const Vec2d pt1 = contour_src[idx_point].cast(); + const Vec2d pt2 = (idx_point + 1 == contour_src.size() ? contour_src.points.front() : contour_src.points[idx_point + 1]).cast(); + const Vec2d pt = lerp(pt1, pt2, it->first.t); + map_infill_end_point_to_boundary[it->second] = std::make_pair(idx_contour, contour_dst.size()); + contour_dst.emplace_back(pt.cast()); + } + } + // Parametrize the curve. + std::vector& contour_data = boundary_data[idx_contour]; + contour_data.reserve(contour_dst.size()); + contour_data.emplace_back(ContourPointData(0.f)); + for (size_t i = 1; i < contour_dst.size(); ++i) + contour_data.emplace_back(contour_data.back().param + (contour_dst[i].cast() - contour_dst[i - 1].cast()).norm()); + contour_data.front().param = contour_data.back().param + (contour_dst.back().cast() - contour_dst.front().cast()).norm(); + } + + assert(boundary.size() == boundary_src.num_contours()); +#if 0 + // Adaptive Cubic Infill produces infill lines, which not always end at the outer boundary. + assert(std::all_of(map_infill_end_point_to_boundary.begin(), map_infill_end_point_to_boundary.end(), + [&boundary](const std::pair& contour_point) { + return contour_point.first < boundary.size() && contour_point.second < boundary[contour_point.first].size(); + })); + assert(boundary_data.size() == boundary_src.holes.size() + 1); +#endif } + + // Mark the points and segments of split boundary as consumed if they are very close to some of the infill line. + { + // @supermerill used 2. * scale_(spacing) + const double clip_distance = 3. * scale_(spacing); + const double distance_colliding = 1.1 * scale_(spacing); + mark_boundary_segments_touching_infill(boundary, boundary_data, bbox, infill_ordered, clip_distance, distance_colliding); + } + + // Connection from end of one infill line to the start of another infill line. + //const float length_max = scale_(spacing); + // const float length_max = scale_((2. / params.density) * spacing); + const coord_t length_max = scale_((1000. / params.density) * spacing); + std::vector merged_with(infill_ordered.size()); + for (size_t i = 0; i < merged_with.size(); ++i) + merged_with[i] = i; + struct ConnectionCost { + ConnectionCost(size_t idx_first, double cost, bool reversed) : idx_first(idx_first), cost(cost), reversed(reversed) {} + size_t idx_first; + double cost; + bool reversed; + }; + std::vector connections_sorted; + connections_sorted.reserve(infill_ordered.size() * 2 - 2); + for (size_t idx_chain = 1; idx_chain < infill_ordered.size(); ++idx_chain) { + const Polyline& pl1 = infill_ordered[idx_chain - 1]; + const Polyline& pl2 = infill_ordered[idx_chain]; + const std::pair* cp1 = &map_infill_end_point_to_boundary[(idx_chain - 1) * 2 + 1]; + const std::pair* cp2 = &map_infill_end_point_to_boundary[idx_chain * 2]; + if (cp1->first != boundary_idx_unconnected && cp1->first == cp2->first) { + // End points on the same contour. Try to connect them. + const std::vector& contour_data = boundary_data[cp1->first]; + float param_lo = (cp1->second == 0) ? 0.f : contour_data[cp1->second].param; + float param_hi = (cp2->second == 0) ? 0.f : contour_data[cp2->second].param; + float param_end = contour_data.front().param; + bool reversed = false; + if (param_lo > param_hi) { + std::swap(param_lo, param_hi); + reversed = true; + } + assert(param_lo >= 0.f && param_lo <= param_end); + assert(param_hi >= 0.f && param_hi <= param_end); + coord_t len = coord_t(param_hi - param_lo); + if (len < length_max) + connections_sorted.emplace_back(idx_chain - 1, len, reversed); + len = coord_t(param_lo + param_end - param_hi); + if (len < length_max) + connections_sorted.emplace_back(idx_chain - 1, len, !reversed); + } + } + std::sort(connections_sorted.begin(), connections_sorted.end(), [](const ConnectionCost& l, const ConnectionCost& r) { return l.cost < r.cost; }); + + //mark point as used depends of connection parameter + if (params.connection == icOuterShell) { + for (auto it = boundary_data.begin() + 1; it != boundary_data.end(); ++it) { + for (ContourPointData& pt : *it) { + pt.point_consumed = true; + } + } + } else if (params.connection == icHoles) { + for (ContourPointData& pt : boundary_data[0]) { + pt.point_consumed = true; + } + } + assert(boundary_data.size() == boundary_src.holes.size() + 1); + + size_t idx_chain_last = 0; + for (ConnectionCost& connection_cost : connections_sorted) { + const std::pair* cp1 = &map_infill_end_point_to_boundary[connection_cost.idx_first * 2 + 1]; + const std::pair* cp1prev = cp1 - 1; + const std::pair* cp2 = &map_infill_end_point_to_boundary[(connection_cost.idx_first + 1) * 2]; + const std::pair* cp2next = cp2 + 1; + assert(cp1->first == cp2->first && cp1->first != boundary_idx_unconnected); + std::vector& contour_data = boundary_data[cp1->first]; + if (connection_cost.reversed) + std::swap(cp1, cp2); + // Mark the the other end points of the segments to be taken as consumed temporarily, so they will not be crossed + // by the new connection line. + bool prev_marked = false; + bool next_marked = false; + if (cp1prev->first == cp1->first && !contour_data[cp1prev->second].point_consumed) { + contour_data[cp1prev->second].point_consumed = true; + prev_marked = true; + } + if (cp2next->first == cp1->first && !contour_data[cp2next->second].point_consumed) { + contour_data[cp2next->second].point_consumed = true; + next_marked = true; + } + if (could_take(contour_data, cp1->second, cp2->second)) { + // Indices of the polygons to be connected. + size_t idx_first = connection_cost.idx_first; + size_t idx_second = idx_first + 1; + for (size_t last = idx_first;;) { + size_t lower = merged_with[last]; + if (lower == last) { + merged_with[idx_first] = lower; + idx_first = lower; + break; + } + last = lower; + } + // Connect the two polygons using the boundary contour. + take(infill_ordered[idx_first], std::move(infill_ordered[idx_second]), boundary[cp1->first], contour_data, cp1->second, cp2->second, connection_cost.reversed); + // Mark the second polygon as merged with the first one. + merged_with[idx_second] = merged_with[idx_first]; + } + if (prev_marked) + contour_data[cp1prev->second].point_consumed = false; + if (next_marked) + contour_data[cp2next->second].point_consumed = false; + } + polylines_out.reserve(polylines_out.size() + std::count_if(infill_ordered.begin(), infill_ordered.end(), [](const Polyline& pl) { return !pl.empty(); })); + for (Polyline& pl : infill_ordered) + if (!pl.empty()) + polylines_out.emplace_back(std::move(pl)); } } +namespace FakePerimeterConnect { // A single T joint of an infill line to a closed contour or one of its holes. struct ContourIntersectionPoint { @@ -1529,7 +2036,8 @@ void mark_boundary_segments_touching_infill( assert(validate_boundary_intersections(boundary_intersections)); } -void Fill::connect_infill(Polylines&& infill_ordered, const ExPolygon& boundary_src, Polylines& polylines_out, const double spacing, const FillParams& params) + +void connect_infill(Polylines&& infill_ordered, const ExPolygon& boundary_src, Polylines& polylines_out, const double spacing, const FillParams& params) { assert(!boundary_src.contour.points.empty()); auto polygons_src = reserve_vector(boundary_src.holes.size() + 1); @@ -1542,7 +2050,7 @@ void Fill::connect_infill(Polylines&& infill_ordered, const ExPolygon& boundary_ connect_infill(std::move(infill_ordered), polygons_src, get_extents(boundary_src.contour), polylines_out, spacing, params); } -void Fill::connect_infill(Polylines&& infill_ordered, const Polygons& boundary_src, const BoundingBox& bbox, Polylines& polylines_out, const double spacing, const FillParams& params) +void connect_infill(Polylines&& infill_ordered, const Polygons& boundary_src, const BoundingBox& bbox, Polylines& polylines_out, const double spacing, const FillParams& params) { auto polygons_src = reserve_vector(boundary_src.size()); for (const Polygon& polygon : boundary_src) @@ -1551,7 +2059,7 @@ void Fill::connect_infill(Polylines&& infill_ordered, const Polygons& boundary_s connect_infill(std::move(infill_ordered), polygons_src, bbox, polylines_out, spacing, params); } -void Fill::connect_infill(Polylines&& infill_ordered, const std::vector& boundary_src, const BoundingBox& bbox, Polylines& polylines_out, const double spacing, const FillParams& params) +void connect_infill(Polylines&& infill_ordered, const std::vector& boundary_src, const BoundingBox& bbox, Polylines& polylines_out, const double spacing, const FillParams& params) { assert(!infill_ordered.empty()); assert(params.anchor_length >= 0.01f); @@ -1914,5 +2422,21 @@ void Fill::connect_infill(Polylines&& infill_ordered, const std::vector _infill_direction(const Surface *surface) const; - void connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const FillParams ¶ms) const; - void do_gap_fill(const ExPolygons &gapfill_areas, const FillParams ¶ms, ExtrusionEntitiesPtr &coll_out) const; ExtrusionRole getRoleFromSurfaceType(const FillParams ¶ms, const Surface *surface) const { @@ -172,9 +170,9 @@ protected: } public: - static void connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const double spacing, const FillParams ¶ms); - static void connect_infill(Polylines &&infill_ordered, const Polygons &boundary, const BoundingBox& bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms); - static void connect_infill(Polylines &&infill_ordered, const std::vector &boundary, const BoundingBox &bbox, Polylines &polylines_out, double spacing, const FillParams ¶ms); + static void connect_infill(Polylines&& infill_ordered, const ExPolygon& boundary, Polylines& polylines_out, const double spacing, const FillParams& params); + //for rectilinear + static void connect_infill(Polylines&& infill_ordered, const ExPolygon& boundary, const Polygons& polygons_src, Polylines& polylines_out, const double spacing, const FillParams& params); static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance); @@ -198,6 +196,17 @@ public: { return Point(_align_to_grid(coord(0), spacing(0), base(0)), _align_to_grid(coord(1), spacing(1), base(1))); } }; +namespace FakePerimeterConnect { + void connect_infill(Polylines&& infill_ordered, const ExPolygon& boundary, Polylines& polylines_out, const double spacing, const FillParams& params); + void connect_infill(Polylines&& infill_ordered, const Polygons& boundary, const BoundingBox& bbox, Polylines& polylines_out, const double spacing, const FillParams& params); + void connect_infill(Polylines&& infill_ordered, const std::vector& boundary, const BoundingBox& bbox, Polylines& polylines_out, double spacing, const FillParams& params); +} +namespace PrusaSimpleConnect { + void connect_infill(Polylines& infill_ordered, const ExPolygon& boundary, Polylines& polylines_out, const double spacing, const FillParams& params); +} +namespace NaiveConnect { + void connect_infill(Polylines&& infill_ordered, const ExPolygon& boundary, Polylines& polylines_out, const double spacing, const FillParams& params); +} class ExtrusionSetRole : public ExtrusionVisitor { ExtrusionRole new_role; diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index 8a9158320..53bd1689b 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -194,7 +194,7 @@ void FillGyroid::_fill_surface_single( if (params.connection == icNotConnected){ append(polylines_out, chain_polylines(polylines)); } else { - this->connect_infill(std::move(polylines), expolygon, polylines_out, this->get_spacing(), params); + this->connect_infill(chain_polylines(polylines), expolygon, polylines_out, this->get_spacing(), params); } // new paths must be rotated back if (std::abs(infill_angle) >= EPSILON) { diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index ed33e3ae5..d9ac66539 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -2889,7 +2889,8 @@ bool FillRectilinear::fill_surface_by_multilines(const Surface* surface, FillPar fill_lines = chain_polylines(std::move(fill_lines)); append(polylines_out, std::move(fill_lines)); } else - connect_infill(std::move(fill_lines), poly_with_offset_base.polygons_outer, get_extents(surface->expolygon.contour), polylines_out, this->get_spacing(), params); + //connect_infill(std::move(fill_lines), poly_with_offset_base.polygons_outer, get_extents(surface->expolygon.contour), polylines_out, this->get_spacing(), params); + connect_infill(std::move(fill_lines), surface->expolygon, poly_with_offset_base.polygons_outer, polylines_out, this->get_spacing(), params); return true; } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index a390c9dee..c21128853 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -559,7 +559,7 @@ const std::vector& Preset::print_options() "perimeter_loop_seam", "seam_angle_cost", "seam_travel_cost", - "infill_connection", + "infill_connection", "infill_connection_solid", "infill_connection_top", "infill_connection_bottom", "first_layer_infill_speed", "thin_walls_min_width", "thin_walls_overlap", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index a0bda6869..88acfd7c4 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1876,7 +1876,8 @@ void PrintConfigDef::init_fff_params() def->label = L("Length of the infill anchor"); def->category = OptionCategory::infill; def->tooltip = L("Connect an infill line to an internal perimeter with a short segment of an additional perimeter. " - "If expressed as percentage (example: 15%) it is calculated over infill extrusion width."); + "If expressed as percentage (example: 15%) it is calculated over infill extrusion width." + "\nIf set to 0, it will use a simpler algo that don't try to create a fake perimeter."); def->sidetext = L("mm or %"); def->ratio_over = "infill_extrusion_width"; def->gui_type = "f_enum_open"; @@ -1886,15 +1887,15 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("5"); def->enum_values.push_back("10"); def->enum_values.push_back("1000"); - def->enum_labels.push_back(L("0 (not anchored)")); + def->enum_labels.push_back(L("0 (Simple connect)")); def->enum_labels.push_back("1 mm"); def->enum_labels.push_back("2 mm"); def->enum_labels.push_back("5 mm"); def->enum_labels.push_back("10 mm"); def->enum_labels.push_back(L("1000 (unlimited)")); def->mode = comAdvanced; -// def->set_default_value(new ConfigOptionFloatOrPercent(300, true)); - def->set_default_value(new ConfigOptionFloatOrPercent(1000, false)); + // def->set_default_value(new ConfigOptionFloatOrPercent(300, true)); + def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); def = this->add("infill_dense", coBool); def->label = (""); @@ -1922,6 +1923,60 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back(L("Not connected")); def->mode = comExpert; def->set_default_value(new ConfigOptionEnum(icConnected)); + + def = this->add("infill_connection_top", coEnum); + def->label = L("Do not connect infill lines to each other"); + def->category = OptionCategory::infill; + def->tooltip = L("Give to the infill algorithm if the infill needs to be connected, and on which periemters" + " Can be useful for art or with high infill/perimeter overlap." + " The result amy varies between infill typers."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("connected"); + def->enum_values.push_back("holes"); + def->enum_values.push_back("outershell"); + def->enum_values.push_back("notconnected"); + def->enum_labels.push_back(L("Connected")); + def->enum_labels.push_back(L("Connected to hole perimeters")); + def->enum_labels.push_back(L("Connected to outer perimeters")); + def->enum_labels.push_back(L("Not connected")); + def->mode = comExpert; + def->set_default_value(new ConfigOptionEnum(icConnected)); + + def = this->add("infill_connection_bottom", coEnum); + def->label = L("Do not connect infill lines to each other"); + def->category = OptionCategory::infill; + def->tooltip = L("Give to the infill algorithm if the infill needs to be connected, and on which periemters" + " Can be useful for art or with high infill/perimeter overlap." + " The result amy varies between infill typers."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("connected"); + def->enum_values.push_back("holes"); + def->enum_values.push_back("outershell"); + def->enum_values.push_back("notconnected"); + def->enum_labels.push_back(L("Connected")); + def->enum_labels.push_back(L("Connected to hole perimeters")); + def->enum_labels.push_back(L("Connected to outer perimeters")); + def->enum_labels.push_back(L("Not connected")); + def->mode = comExpert; + def->set_default_value(new ConfigOptionEnum(icConnected)); + + def = this->add("infill_connection_solid", coEnum); + def->label = L("Do not connect infill lines to each other"); + def->category = OptionCategory::infill; + def->tooltip = L("Give to the infill algorithm if the infill needs to be connected, and on which periemters" + " Can be useful for art or with high infill/perimeter overlap." + " The result amy varies between infill typers."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("connected"); + def->enum_values.push_back("holes"); + def->enum_values.push_back("outershell"); + def->enum_values.push_back("notconnected"); + def->enum_labels.push_back(L("Connected")); + def->enum_labels.push_back(L("Connected to hole perimeters")); + def->enum_labels.push_back(L("Connected to outer perimeters")); + def->enum_labels.push_back(L("Not connected")); + def->mode = comExpert; + def->set_default_value(new ConfigOptionEnum(icConnected)); def = this->add("infill_dense_algo", coEnum); def->label = L("Algorithm"); @@ -3967,7 +4022,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloat(0)); def = this->add("hole_size_compensation", coFloat); - def->label = L("Holes"); + def->label = L("XY compensation"); def->full_label = L("XY holes compensation"); def->category = OptionCategory::slicing; def->tooltip = L("The convex holes will be grown / shrunk in the XY plane by the configured value" @@ -3979,7 +4034,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloat(0)); def = this->add("hole_size_threshold", coFloat); - def->label = L("Holes"); + def->label = L("Threshold"); def->full_label = L("XY holes threshold"); def->category = OptionCategory::slicing; def->tooltip = L("Maximum area for the hole where the hole_size_compensation will apply fully." diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 6c9b4f8bd..30ef74c53 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -754,13 +754,17 @@ public: ConfigOptionFloatOrPercent gap_fill_min_area; ConfigOptionPercent gap_fill_overlap; ConfigOptionFloat gap_fill_speed; - ConfigOptionFloatOrPercent infill_anchor; ConfigOptionBool hole_to_polyhole; + ConfigOptionFloatOrPercent infill_anchor; + ConfigOptionBool hole_to_polyhole; ConfigOptionInt infill_extruder; ConfigOptionFloatOrPercent infill_extrusion_width; ConfigOptionInt infill_every_layers; ConfigOptionFloatOrPercent infill_overlap; ConfigOptionFloat infill_speed; ConfigOptionEnum infill_connection; + ConfigOptionEnum infill_connection_solid; + ConfigOptionEnum infill_connection_top; + ConfigOptionEnum infill_connection_bottom; ConfigOptionBool infill_dense; ConfigOptionEnum infill_dense_algo; ConfigOptionBool infill_first; @@ -859,7 +863,8 @@ protected: OPT_PTR(gap_fill_min_area); OPT_PTR(gap_fill_overlap); OPT_PTR(gap_fill_speed); - OPT_PTR(infill_anchor); OPT_PTR(hole_to_polyhole); + OPT_PTR(infill_anchor); + OPT_PTR(hole_to_polyhole); OPT_PTR(infill_extruder); OPT_PTR(infill_extrusion_width); OPT_PTR(infill_every_layers); @@ -867,6 +872,9 @@ protected: OPT_PTR(infill_speed); OPT_PTR(infill_dense); OPT_PTR(infill_connection); + OPT_PTR(infill_connection_solid); + OPT_PTR(infill_connection_top); + OPT_PTR(infill_connection_bottom); OPT_PTR(infill_dense_algo); OPT_PTR(infill_first); OPT_PTR(ironing); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index e8c04e796..4dbac9aad 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -783,6 +783,10 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector(val); else if (m_opt_id.compare("host_type") == 0) val = idx_from_enum_value(val); - else if (m_opt_id.compare("infill_connection") == 0) + else if (m_opt_id =="infill_connection" || m_opt_id =="infill_connection_solid" + || m_opt_id =="infill_connection_top" || m_opt_id =="infill_connection_bottom") val = idx_from_enum_value(val); else if (m_opt_id.compare("infill_dense_algo") == 0) val = idx_from_enum_value(val); @@ -1164,7 +1165,8 @@ boost::any& Choice::get_value() convert_to_enum_value(ret_enum); else if (m_opt_id.compare("host_type") == 0) convert_to_enum_value(ret_enum); - else if (m_opt_id.compare("infill_connection") == 0) + else if (m_opt_id =="infill_connection" || m_opt_id =="infill_connection_solid" + || m_opt_id =="infill_connection_top" || m_opt_id =="infill_connection_bottom") convert_to_enum_value(ret_enum); else if (m_opt_id.compare("infill_dense_algo") == 0) convert_to_enum_value(ret_enum); diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 617c11801..3c0662d11 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -944,7 +944,8 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config ret = static_cast(config.option>(opt_key)->value); } else if (opt_key == "host_type") { ret = static_cast(config.option>(opt_key)->value); - } else if (opt_key == "infill_connection") { + } else if (opt_key =="infill_connection" || opt_key =="infill_connection_solid" + || opt_key =="infill_connection_top" || opt_key =="infill_connection_bottom") { ret = static_cast(config.option>(opt_key)->value); } else if (opt_key == "infill_dense_algo") { ret = static_cast(config.option>(opt_key)->value); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index c133b4188..04f8c20dc 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -951,7 +951,8 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& return get_string_from_enum(opt_key, config); if (opt_key == "host_type") return get_string_from_enum(opt_key, config); - if (opt_key == "infill_connection") + if (opt_key =="infill_connection" || opt_key =="infill_connection_solid" + || opt_key =="infill_connection_top" || opt_key =="infill_connection_bottom") return get_string_from_enum(opt_key, config); if (opt_key == "infill_dense_algo") return get_string_from_enum(opt_key, config);