diff --git a/.gitignore b/.gitignore index d56825aca..8efb9c54f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ xs/MANIFEST.bak xs/assertlib* .init_bundle.ini local-lib +build* diff --git a/t/thin.t b/t/thin.t index d091117a2..fcab28470 100644 --- a/t/thin.t +++ b/t/thin.t @@ -1,4 +1,4 @@ -use Test::More tests => 28; +use Test::More tests => 29; use strict; use warnings; @@ -102,7 +102,9 @@ if (0) { is scalar(@$res), 1, 'medial axis of a semicircumference is a single line'; # check whether turns are all CCW or all CW - my @lines = @{$res->[0]->lines}; + my @alllines = @{$res->[0]->lines}; + # remove lines taht are near the end. + my @lines = grep($_->a->y >= 1578184 || $_->b->y >= 1578184, @alllines); my @angles = map { $lines[$_-1]->ccw($lines[$_]->b) } 1..$#lines; ok !!(none { $_ < 0 } @angles) || (none { $_ > 0 } @angles), 'all medial axis segments of a semicircumference have the same orientation'; @@ -113,16 +115,25 @@ if (0) { [4.3, 4], [4.3, 0], [4,0], [4,4], [0,4], [0,4.5], [4,4.5], [4,10], [4.3,10], [4.3, 4.5], [6, 4.5], [6,10], [6.2,10], [6.2,4.5], [10,4.5], [10,4], [6.2,4], [6.2,0], [6, 0], [6, 4], )); + $expolygon->contour->make_counter_clockwise(); my $res = $expolygon->medial_axis(scale 0.55, scale 0.25); is scalar(@$res), 2, 'medial axis of a (bit too narrow) french cross is two lines'; ok unscale($res->[0]->length) >= (9.9) - epsilon, 'medial axis has reasonable length'; ok unscale($res->[1]->length) >= (9.9) - epsilon, 'medial axis has reasonable length'; + my @lines1 = @{$res->[0]->lines}; + my @angles1 = map { $lines1[$_-1]->ccw($lines1[$_]->b) } 1..$#lines1; + my @lines2 = @{$res->[1]->lines}; + my @angles2 = map { $lines2[$_-1]->ccw($lines2[$_]->b) } 1..$#lines2; + my @angles = (@angles1, @angles2); + ok !!(none { $_ != 0 } @angles), + 'medial axis of a (bit too narrow) french cross is two lines has only strait lines'; } { my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new_scale( [0.86526705,1.4509841], [0.57696039,1.8637021], [0.4502297,2.5569978], [0.45626199,3.2965596], [1.1218851,3.3049455], [0.96681072,2.8243202], [0.86328971,2.2056997], [0.85367905,1.7790778], )); + $expolygon->contour->make_counter_clockwise(); my $res = $expolygon->medial_axis(scale 1, scale 0.25); is scalar(@$res), 1, 'medial axis of a (bit too narrow) french cross is two lines'; ok unscale($res->[0]->length) >= (1.4) - epsilon, 'medial axis has reasonable length'; diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp index 82541545d..ffcdab1b6 100644 --- a/xs/src/libslic3r/ExPolygon.cpp +++ b/xs/src/libslic3r/ExPolygon.cpp @@ -237,11 +237,7 @@ ExPolygon::remove_point_too_near(const coord_t tolerance) { void ExPolygon::medial_axis(const ExPolygon &bounds, double max_width, double min_width, ThickPolylines* polylines, double height) const { - ExPolygon simplifiedBounds = bounds; - simplifiedBounds.remove_point_too_near(SCALED_RESOLUTION); - ExPolygon simplifiedPolygon = *this; - simplifiedPolygon.remove_point_too_near(SCALED_RESOLUTION); - Slic3r::MedialAxis ma(simplifiedPolygon, simplifiedBounds, max_width, min_width, height); + Slic3r::MedialAxis ma(*this, bounds, max_width, min_width, height); ma.build(polylines); } diff --git a/xs/src/libslic3r/Fill/Fill.cpp b/xs/src/libslic3r/Fill/Fill.cpp index 0158221af..4cec6169a 100644 --- a/xs/src/libslic3r/Fill/Fill.cpp +++ b/xs/src/libslic3r/Fill/Fill.cpp @@ -279,6 +279,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) params.density = 0.01 * density; params.dont_adjust = false; params.fill_exactly = layerm.region()->config.enforce_full_fill_volume.getBool(); + params.dont_connect = layerm.region()->config.infill_not_connected.getBool(); // calculate actual flow from spacing (which might have been adjusted by the infill // pattern generator) diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp index aa9774784..92b70fe56 100644 --- a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -165,7 +165,7 @@ void Fill3DHoneycomb::_fill_surface_single( polylines = intersection_pl(polylines, (Polygons)expolygon); // connect lines - if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections + if (! polylines.empty()) { // prevent calling leftmost_point() on empty collections ExPolygon expolygon_off; { ExPolygons expolygons_off = offset_ex(expolygon, SCALED_EPSILON); @@ -180,7 +180,7 @@ void Fill3DHoneycomb::_fill_surface_single( PolylineCollection::leftmost_point(polylines), false); // reverse allowed bool first = true; for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) { - if (! first) { + if (!params.dont_connect && !first) { // Try to connect the lines. Points &pts_end = polylines_out.back().points; const Point &first_point = it_polyline->points.front(); diff --git a/xs/src/libslic3r/Fill/FillGyroid.cpp b/xs/src/libslic3r/Fill/FillGyroid.cpp index 46d6382f7..31a169e52 100644 --- a/xs/src/libslic3r/Fill/FillGyroid.cpp +++ b/xs/src/libslic3r/Fill/FillGyroid.cpp @@ -154,7 +154,7 @@ void FillGyroid::_fill_surface_single( polylines = intersection_pl(polylines, (Polygons)expolygon); // connect lines - if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections + if (! polylines.empty()) { // prevent calling leftmost_point() on empty collections ExPolygon expolygon_off; { ExPolygons expolygons_off = offset_ex(expolygon, (float)SCALED_EPSILON); @@ -169,7 +169,7 @@ void FillGyroid::_fill_surface_single( PolylineCollection::leftmost_point(polylines), false); // reverse allowed bool first = true; for (Polyline &polyline : chained) { - if (! first) { + if (!params.dont_connect && !first) { // Try to connect the lines. Points &pts_end = polylines_out.back().points; const Point &first_point = polyline.points.front(); diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index a5e8bd954..3b346bf2d 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -1920,7 +1920,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou #endif } } - + // extrude all loops ccw //no! this was decided in perimeter_generator bool was_clockwise = false;// loop.make_counter_clockwise(); @@ -1934,7 +1934,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou Point last_pos = this->last_pos(); if (m_config.spiral_vase) { loop.split_at(last_pos, false); - } else if (seam_position == spNearest || seam_position == spAligned || seam_position == spRear) { + } else if (seam_position == spNearest || seam_position == spAligned || seam_position == spRear || seam_position == spHidden) { Polygon polygon = loop.polygon(); const coordf_t nozzle_dmr = EXTRUDER_CONFIG(nozzle_diameter); const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5); @@ -1949,11 +1949,18 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou last_pos_weight = 1.f; } break; + case spNearest: + last_pos_weight = 5.f; + break; case spRear: last_pos = m_layer->object()->bounding_box().center(); last_pos.y += coord_t(3. * m_layer->object()->bounding_box().radius()); last_pos_weight = 5.f; break; + case spHidden: + last_pos_weight = 0.1f; + break; + } // Insert a projection of last_pos into the polygon. @@ -1975,7 +1982,16 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou const float penaltySeam = 1.3f; const float penaltyOverhangHalf = 10.f; // Penalty for visible seams. - for (size_t i = 0; i < polygon.points.size(); ++ i) { + float dist_max = 0.1f * lengths.back();// 5.f * nozzle_dmr + if (this->config().seam_travel) { + dist_max = 0; + for (size_t i = 0; i < polygon.points.size(); ++i) { + dist_max = std::max(dist_max, (float)polygon.points[i].distance_to(last_pos_proj)); + } + } + //TODO: ignore the angle penalty if the new point is not in an external path (bot/top/ext_peri) + for (size_t i = 0; i < polygon.points.size(); ++i) { + //std::cout << "check point @" << unscale(polygon.points[i].x) << ":" << unscale(polygon.points[i].y); float ccwAngle = penalties[i]; if (was_clockwise) ccwAngle = - ccwAngle; @@ -1996,13 +2012,15 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // Interpolate penalty between maximum and the penalty for a convex vertex. penalty = penaltyConvexVertex + (penaltyFlatSurface - penaltyConvexVertex) * bspline_kernel(ccwAngle * float(PI * 2. / 3.)); } - // Give a negative penalty for points close to the last point or the prefered seam location. - //float dist_to_last_pos_proj = last_pos_proj.distance_to(polygon.points[i]); - float dist_to_last_pos_proj = (i < last_pos_proj_idx) ? - std::min(lengths[last_pos_proj_idx] - lengths[i], lengths.back() - lengths[last_pos_proj_idx] + lengths[i]) : - std::min(lengths[i] - lengths[last_pos_proj_idx], lengths.back() - lengths[i] + lengths[last_pos_proj_idx]); - float dist_max = 0.1f * lengths.back(); // 5.f * nozzle_dmr - penalty -= last_pos_weight * bspline_kernel(dist_to_last_pos_proj / dist_max); + if (this->config().seam_travel) { + penalty += last_pos_weight * polygon.points[i].distance_to(last_pos_proj) / dist_max; + }else{ + // Give a negative penalty for points close to the last point or the prefered seam location. + float dist_to_last_pos_proj = (i < last_pos_proj_idx) ? + std::min(lengths[last_pos_proj_idx] - lengths[i], lengths.back() - lengths[last_pos_proj_idx] + lengths[i]) : + std::min(lengths[i] - lengths[last_pos_proj_idx], lengths.back() - lengths[i] + lengths[last_pos_proj_idx]); + penalty -= last_pos_weight * bspline_kernel(dist_to_last_pos_proj / dist_max); + } penalties[i] = std::max(0.f, penalty); } @@ -2385,7 +2403,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } } if (this->on_first_layer()) - speed = m_config.get_abs_value("first_layer_speed", speed); + if (path.role() == erInternalInfill || path.role() == erSolidInfill) + speed = std::min(m_config.get_abs_value("first_layer_infill_speed", speed), speed); + else + speed = std::min(m_config.get_abs_value("first_layer_speed", speed), speed); if (m_volumetric_speed != 0. && speed == 0) speed = m_volumetric_speed / path.mm3_per_mm; if (m_config.max_volumetric_speed.value > 0) { diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index d3c6b7fdd..597b60e94 100644 --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -643,7 +643,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo "\n\n"); // Ask our writer about how much material was consumed: - m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + if (m_current_tool < m_used_filament_length.size()) + m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); ToolChangeResult result; result.priming = false; @@ -1070,8 +1071,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() m_depth_traversed = m_wipe_tower_depth-m_perimeter_width; - // Ask our writer about how much material was consumed: - m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + // Ask our writer about how much material was consumed. + if (m_current_tool < m_used_filament_length.size()) + m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); ToolChangeResult result; result.priming = false; @@ -1168,7 +1170,6 @@ void WipeTowerPrusaMM::save_on_last_wipe() } } - // Processes vector m_plan and calls respective functions to generate G-code for the wipe tower // Resulting ToolChangeResults are appended into vector "result" void WipeTowerPrusaMM::generate(std::vector> &result) @@ -1258,6 +1259,4 @@ void WipeTowerPrusaMM::make_wipe_tower_square() lay.extra_spacing = lay.depth / lay.toolchanges_depth(); } - - }; // namespace Slic3r diff --git a/xs/src/libslic3r/MedialAxis.cpp b/xs/src/libslic3r/MedialAxis.cpp index 4fbbf5344..7be8c0958 100644 --- a/xs/src/libslic3r/MedialAxis.cpp +++ b/xs/src/libslic3r/MedialAxis.cpp @@ -359,21 +359,21 @@ add_point_same_percent(ThickPolyline* pattern, ThickPolyline* to_modify) /// return 1 for an angle of 90° and 0 for an angle of 0° or 180° double get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t min_dist_between_point) { - double nearestDist = point.distance_to(contour.contour.points.front()); - Point nearest = contour.contour.points.front(); + double nearest_dist = point.distance_to(contour.contour.points.front()); + Point point_nearest = contour.contour.points.front(); size_t id_nearest = 0; - double nearDist = nearestDist; - Point near = nearest; + double near_dist = nearest_dist; + Point point_near = point_nearest; size_t id_near = 0; for (size_t id_point = 1; id_point < contour.contour.points.size(); ++id_point) { - if (nearestDist > point.distance_to(contour.contour.points[id_point])) { - //update near + if (nearest_dist > point.distance_to(contour.contour.points[id_point])) { + //update point_near id_near = id_nearest; - near = nearest; - nearDist = nearestDist; + point_near = point_nearest; + near_dist = nearest_dist; //update nearest - nearestDist = point.distance_to(contour.contour.points[id_point]); - nearest = contour.contour.points[id_point]; + nearest_dist = point.distance_to(contour.contour.points[id_point]); + point_nearest = contour.contour.points[id_point]; id_nearest = id_point; } } @@ -381,7 +381,7 @@ get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t mi size_t id_before = id_nearest == 0 ? contour.contour.points.size() - 1 : id_nearest - 1; Point point_before = id_nearest == 0 ? contour.contour.points.back() : contour.contour.points[id_nearest - 1]; //Search one point far enough to be relevant - while (nearest.distance_to(point_before) < min_dist_between_point) { + while (point_nearest.distance_to(point_before) < min_dist_between_point) { point_before = id_before == 0 ? contour.contour.points.back() : contour.contour.points[id_before - 1]; id_before = id_before == 0 ? contour.contour.points.size() - 1 : id_before - 1; //don't loop @@ -394,7 +394,7 @@ get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t mi size_t id_after = id_nearest == contour.contour.points.size() - 1 ? 0 : id_nearest + 1; Point point_after = id_nearest == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_nearest + 1]; //Search one point far enough to be relevant - while (nearest.distance_to(point_after) < min_dist_between_point) { + while (point_nearest.distance_to(point_after) < min_dist_between_point) { point_after = id_after == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_after + 1]; id_after = id_after == contour.contour.points.size() - 1 ? 0 : id_after + 1; //don't loop @@ -405,15 +405,15 @@ get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t mi } } //compute angle - angle = nearest.ccw_angle(point_before, point_after); + angle = point_nearest.ccw_angle(point_before, point_after); if (angle >= PI) angle = 2 * PI - angle; // smaller angle //compute the diff from 90° angle = abs(angle - PI / 2); - if (near.coincides_with(nearest) && max(nearestDist, nearDist) + SCALED_EPSILON < nearest.distance_to(near)) { + if (point_near.coincides_with(point_nearest) && max(nearest_dist, near_dist) + SCALED_EPSILON < point_nearest.distance_to(point_near)) { //not only nearest Point point_before = id_near == 0 ? contour.contour.points.back() : contour.contour.points[id_near - 1]; Point point_after = id_near == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_near + 1]; - double angle2 = min(nearest.ccw_angle(point_before, point_after), nearest.ccw_angle(point_after, point_before)); + double angle2 = min(point_nearest.ccw_angle(point_before, point_after), point_nearest.ccw_angle(point_after, point_before)); angle2 = abs(angle - PI / 2); angle = (angle + angle2) / 2; } @@ -613,8 +613,12 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con // polyline, after we extend the start point it will be caught by the intersection() // call, so we keep the inner point until we perform the second intersection() as well if (polyline.endpoints.second && !bounds.has_boundary_point(polyline.points.back())) { - Line line(*(polyline.points.end() - 2), polyline.points.back()); - + size_t first_idx = polyline.points.size() - 2; + Line line(*(polyline.points.begin() + first_idx), polyline.points.back()); + while (line.length() < SCALED_RESOLUTION && first_idx>0) { + first_idx--; + line.a = *(polyline.points.begin() + first_idx); + } // prevent the line from touching on the other side, otherwise intersection() might return that solution if (polyline.points.size() == 2) line.a = line.midpoint(); @@ -623,16 +627,39 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con if (this->expolygon.contour.has_boundary_point(polyline.points.back())) { new_back = polyline.points.back(); } else { + //TODO: verify also for holes. (void)this->expolygon.contour.first_intersection(line, &new_back); // safety check if no intersection - if (new_back.x == 0 && new_back.y == 0) return; + if (new_back.x == 0 && new_back.y == 0) { + if (!this->expolygon.contains(line.b)) { + //it's outside!!! + std::cout << "Error, a line is formed that start in a polygon, end outside of it can don't cross it!\n"; + } + new_back = line.b; + } polyline.points.push_back(new_back); polyline.width.push_back(polyline.width.back()); } Point new_bound; + //TODO: verify also for holes. (void)bounds.contour.first_intersection(line, &new_bound); // safety check if no intersection - if (new_bound.x == 0 && new_bound.y == 0) return; + if (new_bound.x == 0 && new_bound.y == 0) { + if (line.b.coincides_with_epsilon(polyline.points.back())) { + return; + } + //check if we don't over-shoot inside us + bool is_in_anchor = false; + for (const ExPolygon& a : anchors) { + if (a.contains(line.b)) { + is_in_anchor = true; + break; + } + } + if (!is_in_anchor) std::cout << "not in anchor:\n"; + if (!is_in_anchor) return; + new_bound = line.b; + } /* if (new_bound.coincides_with_epsilon(new_back)) { return; }*/ @@ -765,7 +792,7 @@ MedialAxis::main_fusion(ThickPolylines& pp) coord_t biggest_main_branch_length = 0; for (size_t k = 0; k < pp.size(); ++k) { //std::cout << "try to find main : " << k << " ? " << i << " " << j << " "; - if (k == i | k == j) continue; + if (k == i || k == j) continue; ThickPolyline& main = pp[k]; if (polyline.first_point().coincides_with(main.last_point())) { main.reverse(); @@ -993,19 +1020,22 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp) while (polyline.points.size() > 1 && polyline.width.front() < this->min_width && polyline.endpoints.first) { //try to split if possible if (polyline.width[1] > min_width) { - double percent_can_keep = (min_width - polyline.width[0]) / (polyline.width[1] - polyline.width[0]); - if (polyline.points.front().distance_to(polyline.points[1]) * percent_can_keep > this->max_width / 2 - && polyline.points.front().distance_to(polyline.points[1])* (1 - percent_can_keep) > this->max_width / 2) { + double percent_can_keep = 1 - (min_width - polyline.width[0]) / (polyline.width[1] - polyline.width[0]); + if (polyline.points.front().distance_to(polyline.points[1]) * percent_can_keep > SCALED_RESOLUTION) { //Can split => move the first point and assign a new weight. //the update of endpoints wil be performed in concatThickPolylines polyline.points.front().x = polyline.points.front().x + - (coord_t)((polyline.points[1].x - polyline.points.front().x) * percent_can_keep); + (coord_t)((polyline.points[1].x - polyline.points.front().x) * (1 - percent_can_keep)); polyline.points.front().y = polyline.points.front().y + - (coord_t)((polyline.points[1].y - polyline.points.front().y) * percent_can_keep); + (coord_t)((polyline.points[1].y - polyline.points.front().y) * (1 - percent_can_keep)); polyline.width.front() = min_width; - changes = true; - break; + } else { + /// almost 0-length, Remove + polyline.points.erase(polyline.points.begin()); + polyline.width.erase(polyline.width.begin()); } + changes = true; + break; } polyline.points.erase(polyline.points.begin()); polyline.width.erase(polyline.width.begin()); @@ -1014,25 +1044,29 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp) while (polyline.points.size() > 1 && polyline.width.back() < this->min_width && polyline.endpoints.second) { //try to split if possible if (polyline.width[polyline.points.size() - 2] > min_width) { - double percent_can_keep = (min_width - polyline.width.back()) / (polyline.width[polyline.points.size() - 2] - polyline.width.back()); - if (polyline.points.back().distance_to(polyline.points[polyline.points.size() - 2]) * percent_can_keep > this->max_width / 2 - && polyline.points.back().distance_to(polyline.points[polyline.points.size() - 2]) * (1 - percent_can_keep) > this->max_width / 2) { + double percent_can_keep = 1 - (min_width - polyline.width.back()) / (polyline.width[polyline.points.size() - 2] - polyline.width.back()); + if (polyline.points.back().distance_to(polyline.points[polyline.points.size() - 2]) * percent_can_keep > SCALED_RESOLUTION) { //Can split => move the first point and assign a new weight. //the update of endpoints wil be performed in concatThickPolylines polyline.points.back().x = polyline.points.back().x + - (coord_t)((polyline.points[polyline.points.size() - 2].x - polyline.points.back().x) * percent_can_keep); + (coord_t)((polyline.points[polyline.points.size() - 2].x - polyline.points.back().x) * (1 - percent_can_keep)); polyline.points.back().y = polyline.points.back().y + - (coord_t)((polyline.points[polyline.points.size() - 2].y - polyline.points.back().y) * percent_can_keep); + (coord_t)((polyline.points[polyline.points.size() - 2].y - polyline.points.back().y) * (1 - percent_can_keep)); polyline.width.back() = min_width; - changes = true; - break; + } else { + /// almost 0-length, Remove + polyline.points.erase(polyline.points.end() - 1); + polyline.width.erase(polyline.width.end() - 1); } + changes = true; + break; } polyline.points.erase(polyline.points.end() - 1); polyline.width.erase(polyline.width.end() - 1); changes = true; } - if (polyline.points.size() < 2) { + //remove points and bits that comes from a "main line" + if (polyline.points.size() < 2 || (changes && polyline.length() < max_width && polyline.points.size() ==2)) { //remove self if too small pp.erase(pp.begin() + i); --i; @@ -1056,7 +1090,6 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp) Optimisation of the old algorithm : now we select the most "strait line" choice when we merge with an other line at a point with more than two meet. */ - bool changes = false; for (size_t i = 0; i < pp.size(); ++i) { ThickPolyline& polyline = pp[i]; if (polyline.endpoints.first && polyline.endpoints.second) continue; // optimization @@ -1066,8 +1099,11 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp) size_t best_idx = 0; // find another polyline starting here - for (size_t j = i + 1; j < pp.size(); ++j) { + for (size_t j = 0; j < pp.size(); ++j) { + if (j == i) continue; ThickPolyline& other = pp[j]; + if (other.endpoints.first && other.endpoints.second) continue; + if (polyline.last_point().coincides_with(other.last_point())) { other.reverse(); } else if (polyline.first_point().coincides_with(other.last_point())) { @@ -1091,16 +1127,14 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp) } } if (best_candidate != nullptr) { - polyline.points.insert(polyline.points.end(), best_candidate->points.begin() + 1, best_candidate->points.end()); polyline.width.insert(polyline.width.end(), best_candidate->width.begin() + 1, best_candidate->width.end()); polyline.endpoints.second = best_candidate->endpoints.second; assert(polyline.width.size() == polyline.points.size()); - changes = true; + if (best_idx < i) i--; pp.erase(pp.begin() + best_idx); } } - if (changes) concatThickPolylines(pp); } void @@ -1276,15 +1310,35 @@ void MedialAxis::build(ThickPolylines* polylines_out) { this->id++; - + //std::cout << layerid << "\n"; + //{ + // stringstream stri; + // stri << "medial_axis_0_enter_" << id << ".svg"; + // SVG svg(stri.str()); + // svg.draw(this->surface); + // svg.Close(); + //} this->expolygon = simplify_polygon_frontier(); + //{ + // stringstream stri; + // stri << "medial_axis_0.5_simplified_" << id << ".svg"; + // SVG svg(stri.str()); + // svg.draw(bounds); + // svg.draw(this->expolygon); + // svg.Close(); + //} + //safety check + if (this->expolygon.area() < this->min_width * this->min_width) this->expolygon = this->surface; + if (this->expolygon.area() < this->min_width * this->min_width) return; - + //std::cout << "simplify_polygon_frontier\n"; // compute the Voronoi diagram and extract medial axis polylines ThickPolylines pp; this->polyline_from_voronoi(this->expolygon.lines(), &pp); + concatThickPolylines(pp); + //std::cout << "concatThickPolylines\n"; //{ // stringstream stri; // stri << "medial_axis_1_voronoi_" << id << ".svg"; @@ -1313,7 +1367,6 @@ MedialAxis::build(ThickPolylines* polylines_out) // svg.Close(); //} - concatThickPolylines(pp); // Aligned fusion: Fusion the bits at the end of lines by "increasing thickness" // For that, we have to find other lines, @@ -1334,6 +1387,16 @@ MedialAxis::build(ThickPolylines* polylines_out) //fusion right-angle corners. fusion_corners(pp); + if (do_not_overextrude) { + const ExPolygons anchors = offset2_ex(diff_ex(this->bounds, this->expolygon), -SCALED_RESOLUTION, SCALED_RESOLUTION); + for (size_t i = 0; i < pp.size(); ++i) { + ThickPolyline& polyline = pp[i]; + extends_line(polyline, anchors, min_width); + polyline.reverse(); + extends_line(polyline, anchors, min_width); + } + } + //reduce extrusion when it's too thin to be printable remove_too_thin_extrusion(pp); //{ @@ -1359,12 +1422,14 @@ MedialAxis::build(ThickPolylines* polylines_out) // Loop through all returned polylines in order to extend their endpoints to the // expolygon boundaries - const ExPolygons anchors = offset2_ex(diff_ex(this->bounds, this->expolygon), -SCALED_RESOLUTION, SCALED_RESOLUTION); - for (size_t i = 0; i < pp.size(); ++i) { - ThickPolyline& polyline = pp[i]; - extends_line(polyline, anchors, min_width); - polyline.reverse(); - extends_line(polyline, anchors, min_width); + if (!do_not_overextrude) { + const ExPolygons anchors = offset2_ex(diff_ex(this->bounds, this->expolygon), -SCALED_RESOLUTION, SCALED_RESOLUTION); + for (size_t i = 0; i < pp.size(); ++i) { + ThickPolyline& polyline = pp[i]; + extends_line(polyline, anchors, min_width); + polyline.reverse(); + extends_line(polyline, anchors, min_width); + } } //{ // stringstream stri; diff --git a/xs/src/libslic3r/MedialAxis.hpp b/xs/src/libslic3r/MedialAxis.hpp index c65cbdb84..560e2bf03 100644 --- a/xs/src/libslic3r/MedialAxis.hpp +++ b/xs/src/libslic3r/MedialAxis.hpp @@ -22,6 +22,7 @@ namespace Slic3r { const double max_width; const double min_width; const double height; + bool do_not_overextrude = true; MedialAxis(const ExPolygon &_expolygon, const ExPolygon &_bounds, const double _max_width, const double _min_width, const double _height) : surface(_expolygon), bounds(_bounds), max_width(_max_width), min_width(_min_width), height(_height) { }; diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index e0097ce2e..9fe855aa1 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -455,10 +455,14 @@ void Model::adjust_min_z() unsigned int Model::get_auto_extruder_id(unsigned int max_extruders) { unsigned int id = s_auto_extruder_id; - - if (++s_auto_extruder_id > max_extruders) + if (id > max_extruders) { + // The current counter is invalid, likely due to switching the printer profiles + // to a profile with a lower number of extruders. reset_auto_extruder_id(); - + id = s_auto_extruder_id; + } else if (++s_auto_extruder_id > max_extruders) { + reset_auto_extruder_id(); + } return id; } diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp index 87b031851..7f96dd523 100644 --- a/xs/src/libslic3r/MultiPoint.hpp +++ b/xs/src/libslic3r/MultiPoint.hpp @@ -45,9 +45,9 @@ public: int idx = -1; if (! this->points.empty()) { idx = 0; - double dist_min = this->points.front().distance_to(point); + double dist_min = this->points.front().distance_to_sq(point); for (int i = 1; i < int(this->points.size()); ++ i) { - double d = this->points[i].distance_to(point); + double d = this->points[i].distance_to_sq(point); if (d < dist_min) { dist_min = d; idx = i; diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index 4801ea654..eeb60773c 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -249,16 +249,18 @@ void PerimeterGenerator::process() // detect edge case where a curve can be split in multiple small chunks. ExPolygons no_thin_onion = offset_ex(last, -(float)(ext_perimeter_width / 2)); - if (no_thin_onion.size()>0 && next_onion.size() > 3 * no_thin_onion.size()) { + float div = 2; + while (no_thin_onion.size() > 0 && next_onion.size() > no_thin_onion.size() && no_thin_onion.size() + next_onion.size() > 3) { + div += 0.5; //use a sightly smaller spacing to try to drastically improve the split ExPolygons next_onion_secondTry = offset2_ex( last, - -(float)(ext_perimeter_width / 2 + ext_min_spacing / 2.5 - 1), - +(float)(ext_min_spacing / 2.5 - 1)); - if (abs(((int32_t)next_onion.size()) - ((int32_t)no_thin_onion.size())) > - 2*abs(((int32_t)next_onion_secondTry.size()) - ((int32_t)no_thin_onion.size()))) { + -(float)(ext_perimeter_width / 2 + ext_min_spacing / div - 1), + +(float)(ext_min_spacing / div - 1)); + if (next_onion.size() > next_onion_secondTry.size()) { next_onion = next_onion_secondTry; } + if (div > 3) break; } // the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width @@ -269,27 +271,32 @@ void PerimeterGenerator::process() // medial axis requires non-overlapping geometry ExPolygons thin_zones = diff_ex(last, no_thin_zone, true); //don't use offset2_ex, because we don't want to merge the zones that have been separated. - ExPolygons expp = offset_ex(thin_zones, (float)(-min_width / 2)); + //a very little bit of overlap can be created here with other thin polygons, but it's more useful than worisome. + ExPolygons half_thins = offset_ex(thin_zones, (float)(-min_width / 2)); + //simplify them + for (ExPolygon &half_thin : half_thins) { + half_thin.remove_point_too_near(SCALED_RESOLUTION); + } //we push the bits removed and put them into what we will use as our anchor - if (expp.size() > 0) { - no_thin_zone = diff_ex(last, offset_ex(expp, (float)(min_width / 2)), true); + if (half_thins.size() > 0) { + no_thin_zone = diff_ex(last, offset_ex(half_thins, (float)(min_width / 2) - SCALED_EPSILON), true); } // compute a bit of overlap to anchor thin walls inside the print. - for (ExPolygon &ex : expp) { + for (ExPolygon &half_thin : half_thins) { //growing back the polygon - //a very little bit of overlap can be created here with other thin polygons, but it's more useful than worisome. - ex.remove_point_too_near(SCALED_RESOLUTION); - ExPolygons ex_bigger = offset_ex(ex, (float)(min_width / 2)); - if (ex_bigger.size() != 1) continue; // impossible error, growing a single polygon can't create multiple or 0. - ExPolygons anchor = intersection_ex(offset_ex(ex, (float)(min_width / 2) + + ExPolygons thin = offset_ex(half_thin, (float)(min_width / 2)); + if (thin.size() != 1) continue; // impossible error, growing a single polygon can't create multiple or 0. + ExPolygons anchor = intersection_ex(offset_ex(half_thin, (float)(min_width / 2) + (float)(ext_perimeter_width / 2), jtSquare), no_thin_zone, true); - ExPolygons bounds = union_ex(ex_bigger, anchor, true); + ExPolygons bounds = union_ex(thin, anchor, true); for (ExPolygon &bound : bounds) { - if (!intersection_ex(ex_bigger[0], bound).empty()) { + if (!intersection_ex(thin[0], bound).empty()) { //be sure it's not too small to extrude reliably - if (ex_bigger[0].area() > min_width*(ext_perimeter_width + ext_perimeter_spacing2)) { + thin[0].remove_point_too_near(SCALED_RESOLUTION); + if (thin[0].area() > min_width*(ext_perimeter_width + ext_perimeter_spacing2)) { + bound.remove_point_too_near(SCALED_RESOLUTION); // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop - ex_bigger[0].medial_axis(bound, ext_perimeter_width + ext_perimeter_spacing2, min_width, + thin[0].medial_axis(bound, ext_perimeter_width + ext_perimeter_spacing2, min_width, &thin_walls, this->layer_height); } break; @@ -644,10 +651,10 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( } PerimeterIntersectionPoint -get_nearest_point(const PerimeterGeneratorLoops &children, ExtrusionLoop &myPolylines, const coord_t dist_cut, const coord_t max_dist) { +PerimeterGenerator::_get_nearest_point(const PerimeterGeneratorLoops &children, ExtrusionLoop &myPolylines, const coord_t dist_cut, const coord_t max_dist) const { //find best points of intersections PerimeterIntersectionPoint intersect; - intersect.distance = 0x7FFFFFFF; + intersect.distance = 0x7FFFFFFF; // ! assumption on intersect type & max value intersect.idx_polyline_outter = -1; intersect.idx_children = -1; for (size_t idx_child = 0; idx_child < children.size(); idx_child++) { @@ -656,23 +663,54 @@ get_nearest_point(const PerimeterGeneratorLoops &children, ExtrusionLoop &myPoly if (myPolylines.paths[idx_poly].extruder_id == (unsigned int)-1) continue; if (myPolylines.paths[idx_poly].length() < dist_cut + SCALED_RESOLUTION) continue; - //first, try to find 2 point near enough - for (size_t idx_point = 0; idx_point < myPolylines.paths[idx_poly].polyline.points.size(); idx_point++) { - const Point &p = myPolylines.paths[idx_poly].polyline.points[idx_point]; - const Point &nearest_p = *child.polygon.closest_point(p); - const coord_t dist = (coord_t)nearest_p.distance_to(p); - if (dist + SCALED_EPSILON / 2 < intersect.distance) { - //ok, copy the idx - intersect.distance = dist; - intersect.idx_children = idx_child; - intersect.idx_polyline_outter = idx_poly; - intersect.outter_best = p; - intersect.child_best = nearest_p; + if ((myPolylines.paths[idx_poly].role() == erExternalPerimeter || child.is_external() ) + && this->object_config->seam_position.value != SeamPosition::spRandom) { + //first, try to find 2 point near enough + for (size_t idx_point = 0; idx_point < myPolylines.paths[idx_poly].polyline.points.size(); idx_point++) { + const Point &p = myPolylines.paths[idx_poly].polyline.points[idx_point]; + const Point &nearest_p = *child.polygon.closest_point(p); + const double dist = nearest_p.distance_to(p); + //Try to find a point in the far side, aligning them + if (dist + dist_cut / 20 < intersect.distance || + (config->perimeter_loop_seam.value == spRear && (intersect.idx_polyline_outter <0 || p.y > intersect.outter_best.y) + && dist <= max_dist && intersect.distance + dist_cut / 20)) { + //ok, copy the idx + intersect.distance = (coord_t)nearest_p.distance_to(p); + intersect.idx_children = idx_child; + intersect.idx_polyline_outter = idx_poly; + intersect.outter_best = p; + intersect.child_best = nearest_p; + } + } + } else { + //first, try to find 2 point near enough + for (size_t idx_point = 0; idx_point < myPolylines.paths[idx_poly].polyline.points.size(); idx_point++) { + const Point &p = myPolylines.paths[idx_poly].polyline.points[idx_point]; + const Point &nearest_p = *child.polygon.closest_point(p); + const double dist = nearest_p.distance_to(p); + if (dist + SCALED_EPSILON < intersect.distance || + (config->perimeter_loop_seam.value == spRear && (intersect.idx_polyline_outter<0 || p.y < intersect.outter_best.y) + && dist <= max_dist && intersect.distance + dist_cut / 20)) { + //ok, copy the idx + intersect.distance = (coord_t)nearest_p.distance_to(p); + intersect.idx_children = idx_child; + intersect.idx_polyline_outter = idx_poly; + intersect.outter_best = p; + intersect.child_best = nearest_p; + } } } - if (intersect.distance <= max_dist) { - return intersect; - } + } + } + if (intersect.distance <= max_dist) { + return intersect; + } + + for (size_t idx_child = 0; idx_child < children.size(); idx_child++) { + const PerimeterGeneratorLoop &child = children[idx_child]; + for (size_t idx_poly = 0; idx_poly < myPolylines.paths.size(); idx_poly++) { + if (myPolylines.paths[idx_poly].extruder_id == (unsigned int)-1) continue; + if (myPolylines.paths[idx_poly].length() < dist_cut + SCALED_RESOLUTION) continue; //second, try to check from one of my points //don't check the last point, as it's used to go outter, can't use it to go inner. @@ -692,9 +730,18 @@ get_nearest_point(const PerimeterGeneratorLoops &children, ExtrusionLoop &myPoly intersect.child_best = nearest_p; } } - if (intersect.distance <= max_dist) { - return intersect; - } + } + } + if (intersect.distance <= max_dist) { + return intersect; + } + + for (size_t idx_child = 0; idx_child < children.size(); idx_child++) { + const PerimeterGeneratorLoop &child = children[idx_child]; + for (size_t idx_poly = 0; idx_poly < myPolylines.paths.size(); idx_poly++) { + if (myPolylines.paths[idx_poly].extruder_id == (unsigned int)-1) continue; + if (myPolylines.paths[idx_poly].length() < dist_cut + SCALED_RESOLUTION) continue; + //lastly, try to check from one of his points for (size_t idx_point = 0; idx_point < child.polygon.points.size(); idx_point++) { const Point &p = child.polygon.points[idx_point]; @@ -898,17 +945,14 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop, //TODO change this->external_perimeter_flow.scaled_width() if it's the first one! const coord_t max_width_extrusion = this->perimeter_flow.scaled_width(); ExtrusionLoop my_loop = _extrude_and_cut_loop(loop, entry_point); - vector path_is_ccw; - - for (size_t idx_poly = 0; idx_poly < my_loop.paths.size(); idx_poly++) { - path_is_ccw.push_back(true); - } + int child_idx = 0; //Polylines myPolylines = { myPolyline }; //iterate on each point ot find the best place to go into the child vector childs = children; while (!childs.empty()) { - PerimeterIntersectionPoint nearest = get_nearest_point(childs, my_loop, this->perimeter_flow.scaled_width(), this->perimeter_flow.scaled_width()* 0.8); + child_idx++; + PerimeterIntersectionPoint nearest = this->_get_nearest_point(childs, my_loop, this->perimeter_flow.scaled_width(), this->perimeter_flow.scaled_width()* 1.42); if (nearest.idx_children == (size_t)-1) { //return ExtrusionEntityCollection(); break; @@ -920,9 +964,7 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop, //PerimeterGeneratorLoops less_childs = childs; //less_childs.erase(less_childs.begin() + nearest.idx_children); //create new node with recursive ask for the inner perimeter & COPY of the points, ready to be cut - const bool cut_path_is_ccw = path_is_ccw[nearest.idx_polyline_outter]; my_loop.paths.insert(my_loop.paths.begin() + nearest.idx_polyline_outter + 1, my_loop.paths[nearest.idx_polyline_outter]); - path_is_ccw.insert(path_is_ccw.begin() + nearest.idx_polyline_outter + 1, cut_path_is_ccw); ExtrusionPath *outer_start = &my_loop.paths[nearest.idx_polyline_outter]; ExtrusionPath *outer_end = &my_loop.paths[nearest.idx_polyline_outter + 1]; @@ -981,7 +1023,6 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop, const size_t child_paths_size = child_loop.paths.size(); if (child_paths_size == 0) continue; my_loop.paths.insert(my_loop.paths.begin() + nearest.idx_polyline_outter + 1, child_loop.paths.begin(), child_loop.paths.end()); - for (size_t i = 0; i < child_paths_size; i++) path_is_ccw.insert(path_is_ccw.begin() + nearest.idx_polyline_outter + 1, !cut_path_is_ccw); //add paths into my_loop => need to re-get the refs outer_start = &my_loop.paths[nearest.idx_polyline_outter]; @@ -1005,8 +1046,8 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop, else inner_start->polyline.clip_start(inner_start->polyline.length()/2); } else { - coord_t length_poly_1 = outer_start->polyline.length(); - coord_t length_poly_2 = outer_end->polyline.length(); + double length_poly_1 = outer_start->polyline.length(); + double length_poly_2 = outer_end->polyline.length(); coord_t length_trim_1 = outer_start_spacing / 2; coord_t length_trim_2 = outer_end_spacing / 2; if (length_poly_1 < length_trim_1) { @@ -1065,9 +1106,9 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop, my_loop.paths[idx].reverse(); } outer_start = &my_loop.paths[nearest.idx_polyline_outter]; - outer_end = &my_loop.paths[nearest.idx_polyline_outter + child_paths_size + 1]; inner_start = &my_loop.paths[nearest.idx_polyline_outter + 1]; inner_end = &my_loop.paths[nearest.idx_polyline_outter + child_paths_size]; + outer_end = &my_loop.paths[nearest.idx_polyline_outter + child_paths_size + 1]; } } @@ -1152,7 +1193,8 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop, travel_path_end[0].polyline.append(outer_end->polyline.points.front()); } //check if we add path or reuse bits - if (outer_start->polyline.points.size() == 1) { + //FIXME + /*if (outer_start->polyline.points.size() == 1) { outer_start->polyline = travel_path_begin.front().polyline; travel_path_begin.erase(travel_path_begin.begin()); outer_start->extruder_id = -1; @@ -1160,15 +1202,13 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop, outer_end->polyline = travel_path_end.back().polyline; travel_path_end.erase(travel_path_end.end() - 1); outer_end->extruder_id = -1; - } + }*/ //add paths into my_loop => after that all ref are wrong! for (int i = travel_path_end.size() - 1; i >= 0; i--) { my_loop.paths.insert(my_loop.paths.begin() + nearest.idx_polyline_outter + child_paths_size + 1, travel_path_end[i]); - path_is_ccw.insert(path_is_ccw.begin() + nearest.idx_polyline_outter + child_paths_size + 1, cut_path_is_ccw); } for (int i = travel_path_begin.size() - 1; i >= 0; i--) { my_loop.paths.insert(my_loop.paths.begin() + nearest.idx_polyline_outter + 1, travel_path_begin[i]); - path_is_ccw.insert(path_is_ccw.begin() + nearest.idx_polyline_outter + 1, cut_path_is_ccw); } } diff --git a/xs/src/libslic3r/PerimeterGenerator.hpp b/xs/src/libslic3r/PerimeterGenerator.hpp index 57d5f7d13..0aae5bdb1 100644 --- a/xs/src/libslic3r/PerimeterGenerator.hpp +++ b/xs/src/libslic3r/PerimeterGenerator.hpp @@ -103,6 +103,7 @@ private: ThickPolylines &thin_walls) const; ExtrusionLoop _traverse_and_join_loops(const PerimeterGeneratorLoop &loop, const PerimeterGeneratorLoops &childs, const Point entryPoint) const; ExtrusionLoop _extrude_and_cut_loop(const PerimeterGeneratorLoop &loop, const Point entryPoint, const Line &direction = Line(Point(0,0),Point(0,0))) const; + PerimeterIntersectionPoint _get_nearest_point(const PerimeterGeneratorLoops &children, ExtrusionLoop &myPolylines, const coord_t dist_cut, const coord_t max_dist) const; ExtrusionEntityCollection _variable_width (const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const; }; diff --git a/xs/src/libslic3r/Polyline.cpp b/xs/src/libslic3r/Polyline.cpp index 53f59c026..346924406 100644 --- a/xs/src/libslic3r/Polyline.cpp +++ b/xs/src/libslic3r/Polyline.cpp @@ -296,6 +296,11 @@ void concatThickPolylines(ThickPolylines& pp) { //concat polyline if only 2 polyline at a point for (size_t i = 0; i < pp.size(); ++i) { ThickPolyline *polyline = &pp[i]; + if (polyline->first_point().coincides_with(polyline->last_point())) { + polyline->endpoints.first = false; + polyline->endpoints.second = false; + continue; + } size_t id_candidate_first_point = -1; size_t id_candidate_last_point = -1; @@ -305,77 +310,78 @@ void concatThickPolylines(ThickPolylines& pp) { for (size_t j = 0; j < pp.size(); ++j) { if (j == i) continue; ThickPolyline *other = &pp[j]; + if (other->first_point().coincides_with(other->last_point())) continue; if (polyline->last_point().coincides_with(other->last_point())) { - other->reverse(); id_candidate_last_point = j; nbCandidate_last_point++; - } else if (polyline->first_point().coincides_with(other->last_point())) { - id_candidate_first_point = j; - nbCandidate_first_point++; - } else if (polyline->first_point().coincides_with(other->first_point())) { - id_candidate_first_point = j; - nbCandidate_first_point++; - other->reverse(); - } else if (polyline->last_point().coincides_with(other->first_point())) { + } + if (polyline->last_point().coincides_with(other->first_point())) { id_candidate_last_point = j; nbCandidate_last_point++; - } else { - continue; + } + if (polyline->first_point().coincides_with(other->last_point())) { + id_candidate_first_point = j; + nbCandidate_first_point++; + } + if (polyline->first_point().coincides_with(other->first_point())) { + id_candidate_first_point = j; + nbCandidate_first_point++; } } if (id_candidate_last_point == id_candidate_first_point && nbCandidate_first_point == 1 && nbCandidate_last_point == 1) { + if (polyline->first_point().coincides_with(pp[id_candidate_first_point].first_point())) pp[id_candidate_first_point].reverse(); // it's a trap! it's a loop! - if (pp[id_candidate_first_point].points.size() > 2) { - polyline->points.insert(polyline->points.begin(), pp[id_candidate_first_point].points.begin() + 1, pp[id_candidate_first_point].points.end() - 1); - polyline->width.insert(polyline->width.begin(), pp[id_candidate_first_point].width.begin() + 1, pp[id_candidate_first_point].width.end() - 1); - } + polyline->points.insert(polyline->points.end(), pp[id_candidate_first_point].points.begin() + 1, pp[id_candidate_first_point].points.end()); + polyline->width.insert(polyline->width.end(), pp[id_candidate_first_point].width.begin() + 1, pp[id_candidate_first_point].width.end()); pp.erase(pp.begin() + id_candidate_first_point); changes = true; polyline->endpoints.first = false; polyline->endpoints.second = false; - continue; - } + } else { - if (nbCandidate_first_point == 1) { - //concat at front - polyline->width[0] = std::max(polyline->width.front(), pp[id_candidate_first_point].width.back()); - polyline->points.insert(polyline->points.begin(), pp[id_candidate_first_point].points.begin(), pp[id_candidate_first_point].points.end() - 1); - polyline->width.insert(polyline->width.begin(), pp[id_candidate_first_point].width.begin(), pp[id_candidate_first_point].width.end() - 1); - polyline->endpoints.first = pp[id_candidate_first_point].endpoints.first; - pp.erase(pp.begin() + id_candidate_first_point); - changes = true; - if (id_candidate_first_point < i) { - i--; - polyline = &pp[i]; + if (nbCandidate_first_point == 1) { + if (polyline->first_point().coincides_with(pp[id_candidate_first_point].first_point())) pp[id_candidate_first_point].reverse(); + //concat at front + polyline->width[0] = std::max(polyline->width.front(), pp[id_candidate_first_point].width.back()); + polyline->points.insert(polyline->points.begin(), pp[id_candidate_first_point].points.begin(), pp[id_candidate_first_point].points.end() - 1); + polyline->width.insert(polyline->width.begin(), pp[id_candidate_first_point].width.begin(), pp[id_candidate_first_point].width.end() - 1); + polyline->endpoints.first = pp[id_candidate_first_point].endpoints.first; + pp.erase(pp.begin() + id_candidate_first_point); + changes = true; + if (id_candidate_first_point < i) { + i--; + polyline = &pp[i]; + } + if (id_candidate_last_point > id_candidate_first_point) { + id_candidate_last_point--; + } + } else if (nbCandidate_first_point == 0) { + //update endpoint + polyline->endpoints.first = true; } - if (id_candidate_last_point > id_candidate_first_point) { - id_candidate_last_point--; + if (nbCandidate_last_point == 1) { + if (polyline->last_point().coincides_with(pp[id_candidate_last_point].last_point())) pp[id_candidate_last_point].reverse(); + //concat at back + polyline->width[polyline->width.size() - 1] = std::max(polyline->width.back(), pp[id_candidate_last_point].width.front()); + polyline->points.insert(polyline->points.end(), pp[id_candidate_last_point].points.begin() + 1, pp[id_candidate_last_point].points.end()); + polyline->width.insert(polyline->width.end(), pp[id_candidate_last_point].width.begin() + 1, pp[id_candidate_last_point].width.end()); + polyline->endpoints.second = pp[id_candidate_last_point].endpoints.second; + pp.erase(pp.begin() + id_candidate_last_point); + changes = true; + if (id_candidate_last_point < i) { + i--; + polyline = &pp[i]; + } + } else if (nbCandidate_last_point == 0) { + //update endpoint + polyline->endpoints.second = true; } - } else if (nbCandidate_first_point == 0 && !polyline->endpoints.first && !polyline->first_point().coincides_with(polyline->last_point())) { - //update endpoint - polyline->endpoints.first = true; - } - if (nbCandidate_last_point == 1) { - //concat at back - polyline->width[polyline->width.size() - 1] = std::max(polyline->width.back(), pp[id_candidate_last_point].width.front()); - polyline->points.insert(polyline->points.end(), pp[id_candidate_last_point].points.begin() + 1, pp[id_candidate_last_point].points.end()); - polyline->width.insert(polyline->width.end(), pp[id_candidate_last_point].width.begin() + 1, pp[id_candidate_last_point].width.end()); - polyline->endpoints.second = pp[id_candidate_last_point].endpoints.second; - pp.erase(pp.begin() + id_candidate_last_point); - changes = true; - if (id_candidate_last_point < i) { - i--; - polyline = &pp[i]; - } - } else if (nbCandidate_last_point == 0 && !polyline->endpoints.second && !polyline->first_point().coincides_with(polyline->last_point())) { - //update endpoint - polyline->endpoints.second = true; - } - if (polyline->last_point().coincides_with(polyline->first_point())) { - //the concat has created a loop : update endpoints - polyline->endpoints.first = false; - polyline->endpoints.second = false; + if (polyline->last_point().coincides_with(polyline->first_point())) { + //the concat has created a loop : update endpoints + polyline->endpoints.first = false; + polyline->endpoints.second = false; + } } } } diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 3f91d4487..ca4db7d65 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -126,6 +126,7 @@ bool Print::invalidate_state_by_config_options(const std::vectorcli = "avoid-crossing-perimeters!"; def->default_value = new ConfigOptionBool(false); + def = this->add("remove_small_gaps", coBool); + def->label = L("Remove small gaps"); + def->tooltip = L("Remove the small gaps in the 3D model when slicing. Disable it if you " + "are very confident on your model, or you want to print an item with a geometry " + "designed for vase mode."); + def->cli = "remove-small-gaps!"; + def->default_value = new ConfigOptionBool(true); + def = this->add("bed_shape", coPoints); def->label = L("Bed shape"); def->default_value = new ConfigOptionPoints { Pointf(0,0), Pointf(200,0), Pointf(200,200), Pointf(0,200) }; @@ -301,7 +309,7 @@ PrintConfigDef::PrintConfigDef() def->default_value = new ConfigOptionBool(false); def = this->add("top_fill_pattern", coEnum); - def->label = L(" Top"); + def->label = L("Top Pattern"); def->category = L("Infill"); def->tooltip = L("Fill pattern for top infill. This only affects the top external visible layer, and not its adjacent solid shells."); def->cli = "top-fill-pattern|solid-fill-pattern=s"; @@ -325,7 +333,7 @@ PrintConfigDef::PrintConfigDef() def->default_value = new ConfigOptionEnum(ipRectilinear); def = this->add("bottom_fill_pattern", coEnum); - def->label = L("Bottom"); + def->label = L("Bottom Pattern"); def->category = L("Infill"); def->tooltip = L("Fill pattern for bottom infill. This only affects the bottom external visible layer, and not its adjacent solid shells."); def->cli = "bottom-fill-pattern|solid-fill-pattern=s"; @@ -401,13 +409,25 @@ PrintConfigDef::PrintConfigDef() def->default_value = new ConfigOptionBool(false); def = this->add("perimeter_loop", coBool); - def->label = L("Looping perimeters"); + def->label = L(" "); def->category = L("Layers and Perimeters"); def->tooltip = L("Join the perimeters to create only one continuous extrusion without any z-hop." " Long inside travel (from external to holes) are not extruded to give some place to the infill."); def->cli = "loop-perimeter!"; def->default_value = new ConfigOptionBool(false); - + + def = this->add("perimeter_loop_seam", coEnum); + def->label = L("Seam position"); + def->category = L("Layers and Perimeters"); + def->tooltip = L("Position of perimeters starting points."); + def->cli = "perimeter-seam-position=s"; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("nearest"); + def->enum_values.push_back("rear"); + def->enum_labels.push_back(L("Nearest")); + def->enum_labels.push_back(L("Rear")); + def->default_value = new ConfigOptionEnum(spRear); + def = this->add("extra_perimeters", coBool); def->label = L("Extra perimeters if needed"); def->category = L("Layers and Perimeters"); @@ -787,8 +807,8 @@ PrintConfigDef::PrintConfigDef() def->default_value = new ConfigOptionPercent(18); def = this->add("fill_pattern", coEnum); - def->label = L("Inside"); - def->category = L("Sparse fill pattern"); + def->label = L("Pattern"); + def->category = L("Infill"); def->tooltip = L("Fill pattern for general low-density infill."); def->cli = "fill-pattern=s"; def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); @@ -861,17 +881,31 @@ PrintConfigDef::PrintConfigDef() def->cli = "first-layer-height=s"; def->ratio_over = "layer_height"; def->default_value = new ConfigOptionFloatOrPercent(0.35, false); - + def = this->add("first_layer_speed", coFloatOrPercent); - def->label = L("First layer speed"); + def->label = L("default"); def->tooltip = L("If expressed as absolute value in mm/s, this speed will be applied to all the print moves " - "of the first layer, regardless of their type. If expressed as a percentage " - "(for example: 40%) it will scale the default speeds."); + "but infill of the first layer, it can be overwrite by the 'default' (default depends of the type of the path) " + "speed if it's lower than that. If expressed as a percentage " + "(for example: 40%) it will scale the 'default' speeds . " + "If expressed as absolute value, it can be overwrite by the 'default' speed if it's lower than that."); def->sidetext = L("mm/s or %"); def->cli = "first-layer-speed=s"; def->min = 0; def->default_value = new ConfigOptionFloatOrPercent(30, false); - + + def = this->add("first_layer_infill_speed", coFloatOrPercent); + def->label = L("infill"); + def->tooltip = L("If expressed as absolute value in mm/s, this speed will be applied to infill moves " + "of the first layer, it can be overwrite by the 'default' (solid infill or infill if not bottom) " + "speed if it's lower than that. If expressed as a percentage " + "(for example: 40%) it will scale the 'default' speed. " + "If expressed as absolute value, it can be overwrite by the 'default' speed if it's lower than that."); + def->sidetext = L("mm/s or %"); + def->cli = "first-layer-infill-speed=s"; + def->min = 0; + def->default_value = new ConfigOptionFloatOrPercent(30, false); + def = this->add("first_layer_temperature", coInts); def->label = L("First layer"); def->tooltip = L("Extruder temperature for first layer. If you want to control temperature manually " @@ -964,6 +998,13 @@ PrintConfigDef::PrintConfigDef() def->cli = "infill-dense!"; def->default_value = new ConfigOptionBool(false); + def = this->add("infill_not_connected", coBool); + def->label = ("Do not connect infill lines to each other."); + def->category = L("Infill"); + def->tooltip = L("If checked, the infill algorithm will try to not connect the lines near the infill. Can be useful for art or with high infill/perimeter overlap."); + def->cli = "infill-not-connected!"; + def->default_value = new ConfigOptionBool(false); + def = this->add("infill_dense_algo", coEnum); def->label = L("Algorithm"); def->tooltip = L("Choose the way the dense layer is lay out." @@ -1644,12 +1685,21 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("nearest"); def->enum_values.push_back("aligned"); def->enum_values.push_back("rear"); + def->enum_values.push_back("hidden"); def->enum_labels.push_back(L("Random")); def->enum_labels.push_back(L("Nearest")); def->enum_labels.push_back(L("Aligned")); - def->enum_labels.push_back(L("Rear")); + def->enum_labels.push_back(L("Rear")); + def->enum_labels.push_back(L("Hidden")); def->default_value = new ConfigOptionEnum(spAligned); + def = this->add("seam_travel", coBool); + def->label = L("Travel move reduced"); + def->category = L("Layers and Perimeters"); + def->tooltip = L("Add a big cost to travel paths when possible (when going into a loop), so it will prefer a less optimal seam posistion if it's nearer."); + def->cli = "seam-travel!"; + def->default_value = new ConfigOptionBool(false); + #if 0 def = this->add("seam_preferred_direction", coFloat); // def->gui_type = "slider"; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index d2cc3166f..d3af29467 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -42,7 +42,7 @@ enum SupportMaterialPattern { }; enum SeamPosition { - spRandom, spNearest, spAligned, spRear + spRandom, spNearest, spAligned, spRear, spHidden }; enum FilamentType { @@ -120,6 +120,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum::get_enum keys_map["nearest"] = spNearest; keys_map["aligned"] = spAligned; keys_map["rear"] = spRear; + keys_map["hidden"] = spHidden; } return keys_map; } @@ -340,6 +341,7 @@ class PrintObjectConfig : public StaticPrintConfig STATIC_PRINT_CONFIG_CACHE(PrintObjectConfig) public: ConfigOptionBool clip_multipart_objects; + ConfigOptionBool remove_small_gaps; ConfigOptionBool dont_support_bridges; ConfigOptionFloat elefant_foot_compensation; ConfigOptionFloatOrPercent extrusion_width; @@ -351,6 +353,7 @@ public: ConfigOptionBool exact_last_layer_height; ConfigOptionInt raft_layers; ConfigOptionEnum seam_position; + ConfigOptionBool seam_travel; // ConfigOptionFloat seam_preferred_direction; // ConfigOptionFloat seam_preferred_direction_jitter; ConfigOptionBool support_material; @@ -387,6 +390,7 @@ protected: void initialize(StaticCacheBase &cache, const char *base_ptr) { OPT_PTR(clip_multipart_objects); + OPT_PTR(remove_small_gaps); OPT_PTR(dont_support_bridges); OPT_PTR(elefant_foot_compensation); OPT_PTR(extrusion_width); @@ -397,6 +401,7 @@ protected: OPT_PTR(exact_last_layer_height); OPT_PTR(raft_layers); OPT_PTR(seam_position); + OPT_PTR(seam_travel); // OPT_PTR(seam_preferred_direction); // OPT_PTR(seam_preferred_direction_jitter); OPT_PTR(support_material); @@ -446,6 +451,7 @@ public: ConfigOptionFloatOrPercent external_perimeter_speed; ConfigOptionBool external_perimeters_first; ConfigOptionBool perimeter_loop; + ConfigOptionEnum perimeter_loop_seam; ConfigOptionBool extra_perimeters; ConfigOptionBool only_one_perimeter_top; ConfigOptionFloat fill_angle; @@ -458,6 +464,7 @@ public: ConfigOptionInt infill_every_layers; ConfigOptionFloatOrPercent infill_overlap; ConfigOptionFloat infill_speed; + ConfigOptionBool infill_not_connected; ConfigOptionBool infill_dense; ConfigOptionEnum infill_dense_algo; ConfigOptionBool infill_first; @@ -502,6 +509,7 @@ protected: OPT_PTR(external_perimeter_speed); OPT_PTR(external_perimeters_first); OPT_PTR(perimeter_loop); + OPT_PTR(perimeter_loop_seam); OPT_PTR(extra_perimeters); OPT_PTR(only_one_perimeter_top); OPT_PTR(fill_angle); @@ -515,6 +523,7 @@ protected: OPT_PTR(infill_overlap); OPT_PTR(infill_speed); OPT_PTR(infill_dense); + OPT_PTR(infill_not_connected); OPT_PTR(infill_dense_algo); OPT_PTR(infill_first); OPT_PTR(overhangs); @@ -755,6 +764,7 @@ public: ConfigOptionInts first_layer_bed_temperature; ConfigOptionFloatOrPercent first_layer_extrusion_width; ConfigOptionFloatOrPercent first_layer_speed; + ConfigOptionFloatOrPercent first_layer_infill_speed; ConfigOptionInts first_layer_temperature; ConfigOptionFloat infill_acceleration; ConfigOptionInts max_fan_speed; @@ -826,6 +836,7 @@ protected: OPT_PTR(first_layer_bed_temperature); OPT_PTR(first_layer_extrusion_width); OPT_PTR(first_layer_speed); + OPT_PTR(first_layer_infill_speed); OPT_PTR(first_layer_temperature); OPT_PTR(infill_acceleration); OPT_PTR(max_fan_speed); diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 4c32a5c78..f0b055903 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -158,6 +158,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector PrintObject::_slice_volumes(const std::vector &z, mesh.translate(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z)); // perform actual slicing TriangleMeshSlicer mslicer(&mesh); + mslicer.safety_offset = (this->config.remove_small_gaps ? scale_(0.0499) : SCALED_EPSILON); mslicer.slice(z, &layers); } } diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 544a5d00b..b8875d07d 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -1792,7 +1792,7 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic //} // perform a safety offset to merge very close facets (TODO: find test case for this) - double safety_offset = scale_(0.0499); + //double safety_offset = scale_(0.0499); // now a config value //FIXME see https://github.com/prusa3d/Slic3r/issues/520 // double safety_offset = scale_(0.0001); diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 24e903c0a..dad5b4010 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -168,6 +168,7 @@ public: const float min_z, const float max_z, IntersectionLine *line_out) const; void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const; + double safety_offset = scale_(0.0499); private: const TriangleMesh *mesh; // Map from a facet to an edge index. diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 2917ef576..dba7152b6 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -14,7 +14,7 @@ #include #define SLIC3R_FORK_NAME "Slic3r++" -#define SLIC3R_VERSION "1.41.1" +#define SLIC3R_VERSION "1.41.2 #define SLIC3R_BUILD "UNKNOWN" typedef int32_t coord_t; diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp index 846e26c45..c990d8f6e 100644 --- a/xs/src/slic3r/GUI/Field.cpp +++ b/xs/src/slic3r/GUI/Field.cpp @@ -527,7 +527,28 @@ void Choice::set_value(const boost::any& value, bool change_event) } else val = 0; - } + } else if (m_opt_id.compare("perimeter_loop_seam") == 0) { + if (!m_opt.enum_values.empty()) { + std::string key; + t_config_enum_values map_names = ConfigOptionEnum::get_enum_values(); + for (auto it : map_names) { + if (val == it.second) { + key = it.first; + break; + } + } + + size_t idx = 0; + for (auto el : m_opt.enum_values) { + if (el.compare(key) == 0) + break; + ++idx; + } + + val = idx == m_opt.enum_values.size() ? 0 : idx; + } else + val = 3; + } dynamic_cast(window)->SetSelection(val); break; } @@ -588,10 +609,18 @@ boost::any& Choice::get_value() else if (m_opt_id.compare("gcode_flavor") == 0) m_value = static_cast(ret_enum); else if (m_opt_id.compare("support_material_pattern") == 0) - m_value = static_cast(ret_enum); - else if (m_opt_id.compare("seam_position") == 0) + m_value = static_cast(ret_enum); + else if (m_opt_id.compare("seam_position") == 0) m_value = static_cast(ret_enum); - else if (m_opt_id.compare("host_type") == 0) + else if (m_opt_id.compare("perimeter_loop_seam") == 0) { + if (!m_opt.enum_values.empty()) { + std::string key = m_opt.enum_values[ret_enum]; + t_config_enum_values map_names = ConfigOptionEnum::get_enum_values(); + int value = map_names.at(key); + m_value = static_cast(value); + } else + m_value = static_cast(3); + } else if (m_opt_id.compare("host_type") == 0) m_value = static_cast(ret_enum); else if (m_opt_id.compare("infill_dense_algo") == 0) m_value = static_cast(ret_enum); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 9219a0fa7..b18fc1804 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -3436,8 +3436,14 @@ void GLCanvas3D::_refresh_if_shown_on_screen() { const Size& cnv_size = get_canvas_size(); _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); - if (m_canvas != nullptr) - m_canvas->Refresh(); + + // Because of performance problems on macOS, where PaintEvents are not delivered + // frequently enough, we call render() here directly when we can. + // We can't do that when m_force_zoom_to_bed_enabled == true, because then render() + // ends up calling back here via _force_zoom_to_bed(), causing a stack overflow. + if (m_canvas != nullptr) { + m_force_zoom_to_bed_enabled ? m_canvas->Refresh() : render(); + } } } diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index ff4ebd280..751af5d66 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -602,9 +602,9 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt else if (opt_key.compare("gcode_flavor") == 0) config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if (opt_key.compare("support_material_pattern") == 0) - config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); - else if (opt_key.compare("seam_position") == 0) - config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); + config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); + else if (opt_key.compare("seam_position") == 0 || opt_key.compare("perimeter_loop_seam") == 0) + config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if (opt_key.compare("host_type") == 0) config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if (opt_key.compare("infill_dense_algo") == 0) diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp index 4c4595517..fe9310fba 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.cpp +++ b/xs/src/slic3r/GUI/OptionsGroup.cpp @@ -458,10 +458,10 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config } else if (opt_key.compare("support_material_pattern") == 0){ ret = static_cast(config.option>(opt_key)->value); - } - else if (opt_key.compare("seam_position") == 0){ - ret = static_cast(config.option>(opt_key)->value); - } + } + else if (opt_key.compare("seam_position") == 0 || opt_key.compare("perimeter_loop_seam") == 0) { + ret = static_cast(config.option>(opt_key)->value); + } else if (opt_key.compare("host_type") == 0){ ret = static_cast(config.option>(opt_key)->value); } diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 420fe267b..60ee2f495 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -307,7 +307,14 @@ const std::vector& Preset::print_options() "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "only_one_perimeter_top", "single_extruder_multi_material_priming", "compatible_printers", "compatible_printers_condition", "inherits", "infill_dense", "infill_dense_algo", "no_perimeter_unsupported", "min_perimeter_unsupported", "noperi_bridge_only", - "support_material_solid_first_layer", "exact_last_layer_height", "perimeter_loop" + "support_material_solid_first_layer" + , "exact_last_layer_height" + , "perimeter_loop", + , "perimeter_loop_seam" + , "seam_travel" + , "remove_small_gaps" + , "infill_not_connected" + ,"first_layer_infill_speed" }; return s_opts; } diff --git a/xs/src/slic3r/GUI/PresetHints.cpp b/xs/src/slic3r/GUI/PresetHints.cpp index aad28766a..69fbc6fb9 100644 --- a/xs/src/slic3r/GUI/PresetHints.cpp +++ b/xs/src/slic3r/GUI/PresetHints.cpp @@ -106,6 +106,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle const auto &support_material_extrusion_width = *print_config.option("support_material_extrusion_width"); const auto &top_infill_extrusion_width = *print_config.option("top_infill_extrusion_width"); const auto &first_layer_speed = *print_config.option("first_layer_speed"); + const auto &first_layer_infill_speed = *print_config.option("first_layer_infill_speed"); // Index of an extruder assigned to a feature. If set to 0, an active extruder will be used for a multi-material print. // If different from idx_extruder, it will not be taken into account for this hint. @@ -142,6 +143,12 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle speed_normal = first_layer_speed.get_abs_value(speed_normal); return (speed_normal > 0.) ? speed_normal : speed_max; }; + auto limit_infill_by_first_layer_speed = [&first_layer_infill_speed, first_layer](double speed_normal, double speed_max) { + if (first_layer && first_layer_infill_speed.value > 0) + // Apply the first layer limit. + speed_normal = first_layer_infill_speed.get_abs_value(speed_normal); + return (speed_normal > 0.) ? speed_normal : speed_max; + }; if (perimeter_extruder_active) { double external_perimeter_rate = Flow::new_from_config_width(frExternalPerimeter, first_positive(first_layer_extrusion_width_ptr, external_perimeter_extrusion_width, extrusion_width), @@ -165,7 +172,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle if (! bridging && infill_extruder_active) { double infill_rate = Flow::new_from_config_width(frInfill, first_positive(first_layer_extrusion_width_ptr, infill_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(infill_speed, max_print_speed); + nozzle_diameter, lh, bfr).mm3_per_mm() * limit_infill_by_first_layer_speed(infill_speed, max_print_speed); if (max_flow < infill_rate) { max_flow = infill_rate; max_flow_extrusion_type = _CHB(L("infill")); @@ -175,7 +182,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle double solid_infill_rate = Flow::new_from_config_width(frInfill, first_positive(first_layer_extrusion_width_ptr, solid_infill_extrusion_width, extrusion_width), nozzle_diameter, lh, 0).mm3_per_mm() * - (bridging ? bridge_speed : limit_by_first_layer_speed(solid_infill_speed, max_print_speed)); + (bridging ? bridge_speed : limit_infill_by_first_layer_speed(solid_infill_speed, max_print_speed)); if (max_flow < solid_infill_rate) { max_flow = solid_infill_rate; max_flow_extrusion_type = _CHB(L("solid infill")); @@ -183,7 +190,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle if (! bridging) { double top_solid_infill_rate = Flow::new_from_config_width(frInfill, first_positive(first_layer_extrusion_width_ptr, top_infill_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(top_solid_infill_speed, max_print_speed); + nozzle_diameter, lh, bfr).mm3_per_mm() * limit_infill_by_first_layer_speed(top_solid_infill_speed, max_print_speed); if (max_flow < top_solid_infill_rate) { max_flow = top_solid_infill_rate; max_flow_extrusion_type = _CHB(L("top solid infill")); diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index c94ba67b1..d5b32c281 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -820,21 +820,30 @@ void TabPrint::build() line.append_option(optgroup->get_option("noperi_bridge_only")); optgroup->append_line(line); - optgroup = page->new_optgroup(_(L("Advanced"))); - optgroup->append_single_option_line("seam_position"); + optgroup = page->new_optgroup(_(L("Advanced"))); + optgroup->append_single_option_line("remove_small_gaps"); + line = { _(L("Avoid unsupported perimeters")), "" }; + line.append_option(optgroup->get_option("seam_position")); + line.append_option(optgroup->get_option("seam_travel")); + optgroup->append_line(line); optgroup->append_single_option_line("external_perimeters_first"); - optgroup->append_single_option_line("perimeter_loop"); + line = { _(L("Looping perimeter")), "" }; + line.append_option(optgroup->get_option("perimeter_loop")); + line.append_option(optgroup->get_option("perimeter_loop_seam")); + optgroup->append_line(line); - page = add_options_page(_(L("Infill")), "infill.png"); - optgroup = page->new_optgroup(_(L("Infill"))); + page = add_options_page(_(L("Infill")), "infill.png"); + optgroup = page->new_optgroup(_(L("Infill"))); optgroup->append_single_option_line("fill_density"); - optgroup->append_single_option_line("fill_pattern"); - line = { _(L("Fill external pattern")), "" }; + line = { _(L("Fill internal")), "" }; + line.append_option(optgroup->get_option("fill_pattern")); + line.append_option(optgroup->get_option("infill_not_connected")); + optgroup->append_line(line); + line = { _(L("Fill external")), "" }; line.append_option(optgroup->get_option("top_fill_pattern")); line.append_option(optgroup->get_option("bottom_fill_pattern")); optgroup->append_line(line); - - optgroup = page->new_optgroup(_(L("Reducing printing time"))); + optgroup = page->new_optgroup(_(L("Reducing printing time"))); optgroup->append_single_option_line("infill_every_layers"); optgroup->append_single_option_line("infill_only_where_needed"); line = { _(L("Suporting dense layer")), "" }; @@ -915,8 +924,11 @@ void TabPrint::build() optgroup = page->new_optgroup(_(L("Speed for non-print moves"))); optgroup->append_single_option_line("travel_speed"); - optgroup = page->new_optgroup(_(L("Modifiers"))); - optgroup->append_single_option_line("first_layer_speed"); + optgroup = page->new_optgroup(_(L("Modifiers"))); + line = { _(L("First layer speed")), "" }; + line.append_option(optgroup->get_option("first_layer_speed")); + line.append_option(optgroup->get_option("first_layer_infill_speed")); + optgroup->append_line(line); optgroup = page->new_optgroup(_(L("Acceleration control (advanced)"))); optgroup->append_single_option_line("perimeter_acceleration"); @@ -1193,11 +1205,13 @@ void TabPrint::update() } } - bool have_perimeters = m_config->opt_int("perimeters") > 0; - for (auto el : {"extra_perimeters", "only_one_perimeter_top", "ensure_vertical_shell_thickness", "thin_walls", "overhangs", - "seam_position", "external_perimeters_first", "external_perimeter_extrusion_width", - "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "perimeter_loop" }) - get_field(el)->toggle(have_perimeters); + bool have_perimeters = m_config->opt_int("perimeters") > 0; + for (auto el : { "extra_perimeters", "only_one_perimeter_top", "ensure_vertical_shell_thickness", "thin_walls", "overhangs", + "seam_position", "external_perimeters_first", "external_perimeter_extrusion_width", + "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "perimeter_loop", "perimeter_loop_seam" }) + get_field(el)->toggle(have_perimeters); + + get_field("perimeter_loop_seam")->toggle(m_config->opt_bool("perimeter_loop")); bool have_no_perimeter_unsupported = have_perimeters && m_config->opt_bool("no_perimeter_unsupported"); for (auto el : { "min_perimeter_unsupported", "noperi_bridge_only" }) diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp index 230fe659e..2fa987f37 100644 --- a/xs/src/slic3r/GUI/Tab.hpp +++ b/xs/src/slic3r/GUI/Tab.hpp @@ -330,7 +330,7 @@ public: size_t m_sys_extruders_count; TabPrinter() {} - TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Printer Settings")), "printer", no_controller) {} + TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Hardware Settings")), "printer", no_controller) {} ~TabPrinter(){} void build() override;