From fee585a0ae5833d3a782cde63afafe950d08ed9a Mon Sep 17 00:00:00 2001 From: supermerill Date: Wed, 29 Dec 2021 01:35:03 +0100 Subject: Fix vase mode: no seam gap. supermerill/SuperSlicer#2114 --- src/libslic3r/GCode.cpp | 174 +++++++++++++++++++++++++----------------------- 1 file changed, 91 insertions(+), 83 deletions(-) (limited to 'src') diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 9279d42f3..5eeddceca 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2917,7 +2917,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s speed = -1; // get a copy; don't modify the orientation of the original loop object otherwise // next copies (if any) would not detect the correct orientation - ExtrusionLoop loop = original_loop; + ExtrusionLoop loop_to_seam = original_loop; if (m_layer->lower_layer != nullptr && lower_layer_edge_grid != nullptr) { if (!*lower_layer_edge_grid) { @@ -2942,31 +2942,31 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s // extrude all loops ccw //no! this was decided in perimeter_generator - bool is_hole_loop = (loop.loop_role() & ExtrusionLoopRole::elrHole) != 0;// loop.make_counter_clockwise(); - bool reverse_turn = loop.polygon().is_clockwise() ^ is_hole_loop; - - split_at_seam_pos(loop, lower_layer_edge_grid, reverse_turn); - - // clip the path to avoid the extruder to get exactly on the first point of the loop; - // if polyline was shorter than the clipping distance we'd get a null polyline, so - // we discard it in that case - coordf_t clip_length = 0; - coordf_t min_clip_length = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)) * 0.15; - if (m_enable_loop_clipping && m_writer.tool_is_extruder()) - clip_length = scale_(m_config.seam_gap.get_abs_value(m_writer.tool()->id(), EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); - - // get paths - ExtrusionPaths paths = loop.paths; - ExtrusionPaths clipped; - if (clip_length > min_clip_length) { - clipped = clip_end(paths, clip_length); - clip_end(clipped, min_clip_length); - for (ExtrusionPath& ep : clipped) - ep.mm3_per_mm = 0; - append(paths, clipped); - } else { - clip_end(paths, clip_length); + bool is_hole_loop = (loop_to_seam.loop_role() & ExtrusionLoopRole::elrHole) != 0;// loop.make_counter_clockwise(); + bool reverse_turn = loop_to_seam.polygon().is_clockwise() ^ is_hole_loop; + + split_at_seam_pos(loop_to_seam, lower_layer_edge_grid, reverse_turn); + const coordf_t full_loop_dist = loop_to_seam.length(); + + // don't clip the path ? + ExtrusionPaths &paths = loop_to_seam.paths; + if (false && m_enable_loop_clipping && m_writer.tool_is_extruder()) { + coordf_t clip_length = scale_(m_config.seam_gap.get_abs_value(m_writer.tool()->id(), EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); + coordf_t min_clip_length = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)) * 0.15; + + // get paths + ExtrusionPaths clipped; + if (clip_length > min_clip_length) { + clipped = clip_end(paths, clip_length); + clip_end(clipped, min_clip_length); + for (ExtrusionPath& ep : clipped) + ep.mm3_per_mm = 0; + append(paths, clipped); + } else { + clip_end(paths, clip_length); + } } + if (paths.empty()) return ""; // apply the small/external? perimeter speed @@ -2974,12 +2974,12 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s coordf_t min_length = scale_d(this->m_config.small_perimeter_min_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); coordf_t max_length = scale_d(this->m_config.small_perimeter_max_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); max_length = std::max(min_length, max_length); - if (loop.length() < max_length) { - if (loop.length() <= min_length) { + if (full_loop_dist < max_length) { + if (full_loop_dist <= min_length) { speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed); } else if (max_length > min_length) { //use a negative speed: it will be use as a ratio when computing the real speed - speed = -(loop.length() - min_length) / (max_length - min_length); + speed = -(full_loop_dist - min_length) / (max_length - min_length); } } } @@ -3090,7 +3090,7 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s z_per_length * line_length / nb_sections }; coordf_t current_height_internal = current_height + height_increment / 2; //ensure you go to the good xyz - if ((last_point - previous).norm() > EPSILON) + if( (last_point - previous).norm() > EPSILON) gcode += m_writer.extrude_to_xyz(last_point, 0, description); //extrusions for (int i = 0; i < nb_sections - 1; i++) { @@ -3245,7 +3245,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s // get a copy; don't modify the orientation of the original loop object otherwise // next copies (if any) would not detect the correct orientation - ExtrusionLoop loop = original_loop; + ExtrusionLoop loop_to_seam = original_loop; if (m_layer->lower_layer != nullptr && lower_layer_edge_grid != nullptr) { if (! *lower_layer_edge_grid) { @@ -3271,48 +3271,55 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s // extrude all loops ccw //no! this was decided in perimeter_generator //but we need to know where is "inside", so we will use is_hole_loop. if is_hole_loop, then we need toconsider that the right direction is clockwise, else counter clockwise. - bool is_hole_loop = (loop.loop_role() & ExtrusionLoopRole::elrHole) != 0;// loop.make_counter_clockwise(); + bool is_hole_loop = (loop_to_seam.loop_role() & ExtrusionLoopRole::elrHole) != 0;// loop.make_counter_clockwise(); //if spiral vase, we have to ensure that all loops are in the same orientation. if (this->m_config.spiral_vase) { - loop.make_counter_clockwise(); + loop_to_seam.make_counter_clockwise(); is_hole_loop = false; } - split_at_seam_pos(loop, lower_layer_edge_grid, is_hole_loop); + split_at_seam_pos(loop_to_seam, lower_layer_edge_grid, is_hole_loop); + const coordf_t full_loop_length = loop_to_seam.length(); + const bool is_full_loop_ccw = loop_to_seam.polygon().is_counter_clockwise(); + //after that point, loop_to_seam can be modified by 'paths', so don't use it anymore // clip the path to avoid the extruder to get exactly on the first point of the loop; // if polyline was shorter than the clipping distance we'd get a null polyline, so // we discard it in that case - coordf_t clip_length = 0; - coordf_t min_clip_length = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)) * 0.15; - if (m_enable_loop_clipping && m_writer.tool_is_extruder()) - clip_length = scale_(m_config.seam_gap.get_abs_value(m_writer.tool()->id(), EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); - - // get paths - ExtrusionPaths paths = loop.paths; - ExtrusionPaths clipped; - if (clip_length > min_clip_length) { - clipped = clip_end(paths, clip_length); - clip_end(clipped, min_clip_length); - for (ExtrusionPath& ep : clipped) - ep.mm3_per_mm = 0; - append(paths, clipped); - } else { - clip_end(paths, clip_length); + ExtrusionPaths& paths = loop_to_seam.paths; + if (m_enable_loop_clipping && m_writer.tool_is_extruder()) { + coordf_t clip_length = scale_(m_config.seam_gap.get_abs_value(m_writer.tool()->id(), EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); + coordf_t min_clip_length = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)) * 0.15; + + // get paths + ExtrusionPaths clipped; + if (clip_length > min_clip_length) { + // remove clip_length, like normally, but keep the removed part + clipped = clip_end(paths, clip_length); + // remove min_clip_length from the removed paths + clip_end(clipped, min_clip_length); + // ensure that the removed paths are travels + for (ExtrusionPath& ep : clipped) + ep.mm3_per_mm = 0; + // re-add removed paths as travels. + append(paths, clipped); + } else { + clip_end(paths, clip_length); + } } if (paths.empty()) return ""; // apply the small perimeter speed - if (speed == -1 && is_perimeter(paths.front().role()) && loop.length() <= + if (speed == -1 && is_perimeter(paths.front().role()) && full_loop_length <= scale_(this->m_config.small_perimeter_max_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)))) { double min_length = scale_d(this->m_config.small_perimeter_min_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); double max_length = scale_d(this->m_config.small_perimeter_max_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); - if (loop.length() <= min_length) { + if (full_loop_length <= min_length) { speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed); } else { //set speed between -1 and 0 you have to multiply the real peed by the opposite of that, and add the other part as small_perimeter_speed - speed = (min_length - loop.length()) / (max_length - min_length); + speed = (min_length - full_loop_length) / (max_length - min_length); } } @@ -3331,8 +3338,9 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s if (m_wipe.enable) m_wipe.path = paths.front().polyline; // TODO: don't limit wipe to last path - //wipe for External Perimeter - if (paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 0 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 2) { + //wipe for External Perimeter (and not vase) + if (paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 0 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 2 + && (m_enable_loop_clipping && m_writer.tool_is_extruder()) ) { //get points for wipe Point prev_point = *(paths.back().polyline.points.end() - 2); // second to last point // *(paths.back().polyline.points.end() - 2) this is the same as (or should be) as paths.front().first_point(); @@ -3342,39 +3350,39 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s gcode += ";" + GCodeProcessor::Wipe_Start_Tag + "\n"; //extra wipe before the little move. if (EXTRUDER_CONFIG_WITH_DEFAULT(wipe_extra_perimeter, 0) > 0) { - coordf_t wipe_dist = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(wipe_extra_perimeter,0)); + coordf_t wipe_dist = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(wipe_extra_perimeter, 0)); ExtrusionPaths paths_wipe; m_wipe.reset_path(); for (int i = 0; i < paths.size(); i++) { ExtrusionPath& path = paths[i]; if (wipe_dist > 0) { //first, we use the polyline for wipe_extra_perimeter - if (path.length() < wipe_dist) { - wipe_dist -= path.length(); - paths_wipe.push_back(path); - } else { - paths_wipe.push_back(path); - paths_wipe.back().clip_end(path.length() - wipe_dist); - - ExtrusionPath next_point_path = path; - next_point_path.reverse(); - next_point_path.clip_end(wipe_dist); - next_point_path.reverse(); - if (next_point_path.size() > 1) { - next_point = next_point_path.polyline.points[1]; - } else if (i + 1 < paths.size()) { - next_point = paths[i + 1].first_point(); + if (path.length() < wipe_dist) { + wipe_dist -= path.length(); + paths_wipe.push_back(path); } else { - next_point = paths[0].first_point(); - } + paths_wipe.push_back(path); + paths_wipe.back().clip_end(path.length() - wipe_dist); + + ExtrusionPath next_point_path = path; + next_point_path.reverse(); + next_point_path.clip_end(wipe_dist); + next_point_path.reverse(); + if (next_point_path.size() > 1) { + next_point = next_point_path.polyline.points[1]; + } else if (i + 1 < paths.size()) { + next_point = paths[i + 1].first_point(); + } else { + next_point = paths[0].first_point(); + } m_wipe.path.append(path.polyline); m_wipe.path.clip_start(wipe_dist); wipe_dist -= path.length(); - } + } } else { //then, it's stored for the wipe on retract m_wipe.path.append(path.polyline); - } + } } //move for (ExtrusionPath& path : paths_wipe) { @@ -3395,22 +3403,22 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s //FIXME improve the algorithm in case the loop is split into segments with a low number of points (see the Point b query). Point a = next_point; // second point Point b = prev_point; // second to last point - if (is_hole_loop ? loop.polygon().is_counter_clockwise() : loop.polygon().is_clockwise()) { + if (is_hole_loop ? is_full_loop_ccw : (!is_full_loop_ccw)) { // swap points Point c = a; a = b; b = c; } double angle = current_point.ccw_angle(a, b) / 3; - + // turn left if contour, turn right if hole - if (is_hole_loop ? loop.polygon().is_counter_clockwise() : loop.polygon().is_clockwise()) angle *= -1; + if (is_hole_loop ? is_full_loop_ccw : (!is_full_loop_ccw)) angle *= -1; // create the destination point along the first segment and rotate it // we make sure we don't exceed the segment length because we don't know // the rotation of the second segment so we might cross the object boundary Vec2d current_pos = current_point.cast(); Vec2d next_pos = next_point.cast(); - Vec2d vec_dist = next_pos - current_pos; - const coordf_t nd = scale_d(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter,0)); + Vec2d vec_dist = next_pos - current_pos; + const coordf_t nd = scale_d(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)); double l2 = vec_dist.squaredNorm(); // Shift by no more than a nozzle diameter. //FIXME Hiding the seams will not work nicely for very densely discretized contours! @@ -3426,11 +3434,11 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s current_pos = pt.cast(); //go to the inside (use clipper for easy shift) Polygon original_polygon = original_loop.polygon(); - Polygons polys = offset(original_polygon, (original_polygon.is_clockwise()?1:-1) * nd / 2); + Polygons polys = offset(original_polygon, (original_polygon.is_clockwise() ? 1 : -1) * nd / 2); //find nearest point size_t best_poly_idx = 0; size_t best_pt_idx = 0; - coordf_t best_sqr_dist = nd*nd*2; + coordf_t best_sqr_dist = nd * nd * 2; for (size_t poly_idx = 0; poly_idx < polys.size(); poly_idx++) { Polygon& poly = polys[poly_idx]; if (poly.is_clockwise() ^ original_polygon.is_clockwise()) @@ -3440,7 +3448,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s best_sqr_dist = poly.points[pt_idx].distance_to_square(pt); best_poly_idx = poly_idx; best_pt_idx = pt_idx; - } + } } } if (best_sqr_dist == nd * nd * 2) { @@ -3450,7 +3458,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s if (poly.is_clockwise() ^ original_polygon.is_clockwise()) poly.reverse(); poly.points.push_back(poly.points.front()); - for (size_t pt_idx = 0; pt_idx < poly.points.size()-1; pt_idx++) { + for (size_t pt_idx = 0; pt_idx < poly.points.size() - 1; pt_idx++) { if (Line{ poly.points[pt_idx], poly.points[pt_idx + 1] }.distance_to_squared(pt) < best_sqr_dist) { poly.points.insert(poly.points.begin() + pt_idx + 1, pt); best_sqr_dist = 0; -- cgit v1.2.3