From 98c011d59bf63415eb0c279a799d4db78263144b Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 26 Jul 2023 11:24:32 +0200 Subject: [PATCH] ArcWelder: Improved resolution of arc discretization in G-code preview --- src/libslic3r/GCode.cpp | 9 +++++--- src/libslic3r/GCode/GCodeProcessor.cpp | 24 ++++++++++----------- src/libslic3r/GCode/GCodeProcessor.hpp | 11 +++++++++- src/libslic3r/GCode/GCodeWriter.cpp | 15 +++++++++++++ src/libslic3r/GCode/GCodeWriter.hpp | 6 ++++-- src/libslic3r/GCode/Wipe.cpp | 2 ++ src/libslic3r/Geometry/ArcWelder.cpp | 5 +++-- src/libslic3r/Geometry/ArcWelder.hpp | 30 ++++++++++++++++++++++++-- 8 files changed, 79 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 168dad84ce..eff430fdb0 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2902,9 +2902,10 @@ std::string GCodeGenerator::_extrude( Vec2d p = this->point_to_gcode_quantized(it->point); if (it->radius == 0) { // Extrude line segment. - const double line_length = (p - prev).norm(); - path_length += line_length; - gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment); + if (const double line_length = (p - prev).norm(); line_length > 0) { + path_length += line_length; + gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment); + } } else { // Extrude an arc. assert(m_config.arc_fitting == ArcFittingType::EmitCenter || @@ -2920,9 +2921,11 @@ std::string GCodeGenerator::_extrude( ij = GCodeFormatter::quantize(center_raw); } double angle = Geometry::ArcWelder::arc_angle(prev.cast(), p.cast(), double(radius)); + assert(angle > 0); const double line_length = angle * std::abs(radius); path_length += line_length; const double dE = e_per_mm * line_length; + assert(dE > 0); gcode += emit_radius ? m_writer.extrude_to_xy_G2G3R(p, radius, it->ccw(), dE, comment) : m_writer.extrude_to_xy_G2G3IJ(p, ij, it->ccw(), dE, comment); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index e654d88f93..28cfc8a515 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -44,8 +44,6 @@ static const Slic3r::Vec3f DEFAULT_EXTRUDER_OFFSET = Slic3r::Vec3f::Zero(); // taken from PrusaResearch.ini - [printer:Original Prusa i3 MK2.5 MMU2] static const std::vector DEFAULT_EXTRUDER_COLORS = { "#FF8000", "#DB5182", "#3EC0FF", "#FF4F4F", "#FBEB7D" }; -static const std::string INTERNAL_G2G3_TAG = "!#!#! internal only - from G2/G3 expansion !#!#!"; - namespace Slic3r { const std::vector GCodeProcessor::Reserved_Tags = { @@ -2364,13 +2362,10 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) if (line.has_e()) g1_axes[E] = (double)line.e(); std::optional g1_feedrate = std::nullopt; if (line.has_f()) g1_feedrate = (double)line.f(); - std::optional g1_cmt = std::nullopt; - if (!line.comment().empty()) g1_cmt = line.comment(); - - process_G1(g1_axes, g1_feedrate, g1_cmt); + process_G1(g1_axes, g1_feedrate); } -void GCodeProcessor::process_G1(const std::array, 4>& axes, std::optional feedrate, std::optional cmt) +void GCodeProcessor::process_G1(const std::array, 4>& axes, std::optional feedrate, G1DiscretizationOrigin origin) { const float filament_diameter = (static_cast(m_extruder_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[m_extruder_id] : m_result.filament_diameters.back(); const float filament_radius = 0.5f * filament_diameter; @@ -2453,7 +2448,7 @@ void GCodeProcessor::process_G1(const std::array, 4>& axes m_height = m_forced_height; else if (m_layer_id == 0) m_height = m_first_layer_height + m_z_offset; - else if (!cmt.has_value() || *cmt != INTERNAL_G2G3_TAG) { + else if (origin == G1DiscretizationOrigin::G1) { if (m_end_position[Z] > m_extruded_last_z + EPSILON && delta_pos[Z] == 0.0) m_height = m_end_position[Z] - m_extruded_last_z; } @@ -2464,7 +2459,7 @@ void GCodeProcessor::process_G1(const std::array, 4>& axes if (m_end_position[Z] == 0.0f || (m_extrusion_role == GCodeExtrusionRole::Custom && m_layer_id == 0)) m_end_position[Z] = m_height; - if (!cmt.has_value() || *cmt != INTERNAL_G2G3_TAG) + if (origin == G1DiscretizationOrigin::G1) m_extruded_last_z = m_end_position[Z]; m_options_z_corrector.update(m_height); @@ -2694,7 +2689,7 @@ void GCodeProcessor::process_G1(const std::array, 4>& axes } // store move - store_move_vertex(type, cmt.has_value() && *cmt == INTERNAL_G2G3_TAG); + store_move_vertex(type, origin == G1DiscretizationOrigin::G2G3); } void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise) @@ -2836,9 +2831,7 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc g1_feedrate = (double)*feedrate; if (extrusion.has_value()) g1_axes[E] = target[E]; - std::optional g1_cmt = INTERNAL_G2G3_TAG; - - process_G1(g1_axes, g1_feedrate, g1_cmt); + process_G1(g1_axes, g1_feedrate, G1DiscretizationOrigin::G2G3); }; // calculate arc segments @@ -2847,8 +2840,13 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc // https://github.com/prusa3d/Prusa-Firmware/blob/MK3/Firmware/motion_control.cpp // segments count +#if 0 static const double MM_PER_ARC_SEGMENT = 1.0; const size_t segments = std::max(std::floor(travel_length / MM_PER_ARC_SEGMENT), 1); +#else + static const double gcode_arc_tolerance = 0.0125; + const size_t segments = Geometry::ArcWelder::arc_discretization_steps(arc.start_radius(), std::abs(arc.angle), gcode_arc_tolerance); +#endif const double inv_segment = 1.0 / double(segments); const double theta_per_segment = arc.angle * inv_segment; diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index fcba4321ae..a383f251c2 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -56,9 +56,13 @@ namespace Slic3r { time = 0.0f; travel_time = 0.0f; custom_gcode_times.clear(); + custom_gcode_times.shrink_to_fit(); moves_times.clear(); + moves_times.shrink_to_fit(); roles_times.clear(); + roles_times.shrink_to_fit(); layers_times.clear(); + layers_times.shrink_to_fit(); } }; @@ -76,6 +80,7 @@ namespace Slic3r { m.reset(); } volumes_per_color_change.clear(); + volumes_per_color_change.shrink_to_fit(); volumes_per_extruder.clear(); used_filaments_per_role.clear(); cost_per_extruder.clear(); @@ -680,8 +685,12 @@ namespace Slic3r { // Move void process_G0(const GCodeReader::GCodeLine& line); void process_G1(const GCodeReader::GCodeLine& line); + enum class G1DiscretizationOrigin { + G1, + G2G3, + }; void process_G1(const std::array, 4>& axes = { std::nullopt, std::nullopt, std::nullopt, std::nullopt }, - std::optional feedrate = std::nullopt, std::optional cmt = std::nullopt); + std::optional feedrate = std::nullopt, G1DiscretizationOrigin origin = G1DiscretizationOrigin::G1); // Arc Move void process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise); diff --git a/src/libslic3r/GCode/GCodeWriter.cpp b/src/libslic3r/GCode/GCodeWriter.cpp index 2dad407caa..241daa3250 100644 --- a/src/libslic3r/GCode/GCodeWriter.cpp +++ b/src/libslic3r/GCode/GCodeWriter.cpp @@ -274,6 +274,12 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string_view std::string GCodeWriter::travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, const std::string_view comment) { + assert(std::abs(point.x()) < 1200.); + assert(std::abs(point.y()) < 1200.); + assert(std::abs(ij.x()) < 1200.); + assert(std::abs(ij.y()) < 1200.); + assert(std::abs(ij.x()) >= 0.001 || std::abs(ij.y()) >= 0.001); + m_pos.head<2>() = point.head<2>(); GCodeG2G3Formatter w(ccw); @@ -285,6 +291,11 @@ std::string GCodeWriter::travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij std::string GCodeWriter::travel_to_xy_G2G3R(const Vec2d &point, const double radius, const bool ccw, const std::string_view comment) { + assert(std::abs(point.x()) < 1200.); + assert(std::abs(point.y()) < 1200.); + assert(std::abs(radius) >= 0.001); + assert(std::abs(radius) < 1800.); + m_pos.head<2>() = point.head<2>(); GCodeG2G3Formatter w(ccw); @@ -395,6 +406,8 @@ std::string GCodeWriter::extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &i assert(std::abs(point.y()) < 1200.); assert(std::abs(ij.x()) < 1200.); assert(std::abs(ij.y()) < 1200.); + assert(std::abs(ij.x()) >= 0.001 || std::abs(ij.y()) >= 0.001); + m_pos.head<2>() = point.head<2>(); GCodeG2G3Formatter w(ccw); @@ -411,7 +424,9 @@ std::string GCodeWriter::extrude_to_xy_G2G3R(const Vec2d &point, const double ra assert(std::abs(dE) < 1000.0); assert(std::abs(point.x()) < 1200.); assert(std::abs(point.y()) < 1200.); + assert(std::abs(radius) >= 0.001); assert(std::abs(radius) < 1800.); + m_pos.head<2>() = point.head<2>(); GCodeG2G3Formatter w(ccw); diff --git a/src/libslic3r/GCode/GCodeWriter.hpp b/src/libslic3r/GCode/GCodeWriter.hpp index 09a68b92de..127fb61c03 100644 --- a/src/libslic3r/GCode/GCodeWriter.hpp +++ b/src/libslic3r/GCode/GCodeWriter.hpp @@ -181,8 +181,10 @@ public: } void emit_ij(const Vec2d &point) { - this->emit_axis('I', point.x(), XYZF_EXPORT_DIGITS); - this->emit_axis('J', point.y(), XYZF_EXPORT_DIGITS); + if (point.x() != 0) + this->emit_axis('I', point.x(), XYZF_EXPORT_DIGITS); + if (point.y() != 0) + this->emit_axis('J', point.y(), XYZF_EXPORT_DIGITS); } // Positive radius means a smaller arc, diff --git a/src/libslic3r/GCode/Wipe.cpp b/src/libslic3r/GCode/Wipe.cpp index 561e86a835..0fa887810f 100644 --- a/src/libslic3r/GCode/Wipe.cpp +++ b/src/libslic3r/GCode/Wipe.cpp @@ -128,6 +128,7 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) double radius = emit_radius ? GCodeFormatter::quantize_xyzf(radius_in) : radius_in; Vec2d center = Geometry::ArcWelder::arc_center(prev_quantized.cast(), p_quantized.cast(), double(radius), ccw); float angle = Geometry::ArcWelder::arc_angle(prev_quantized.cast(), p_quantized.cast(), double(radius)); + assert(angle > 0); double segment_length = angle * std::abs(radius); double dE = GCodeFormatter::quantize_e(xy_to_e * segment_length); bool done = false; @@ -146,6 +147,7 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) done = true; } else p = p_quantized; + assert(dE > 0); if (emit_radius) { gcode += gcodegen.writer().extrude_to_xy_G2G3R(p, radius, ccw, -dE, wipe_retract_comment); } else { diff --git a/src/libslic3r/Geometry/ArcWelder.cpp b/src/libslic3r/Geometry/ArcWelder.cpp index bb23381de8..4f789f3c50 100644 --- a/src/libslic3r/Geometry/ArcWelder.cpp +++ b/src/libslic3r/Geometry/ArcWelder.cpp @@ -41,10 +41,11 @@ Points arc_discretize(const Point &p1, const Point &p2, const double radius, con { Vec2d center = arc_center(p1.cast(), p2.cast(), radius, ccw); double angle = arc_angle(p1.cast(), p2.cast(), radius); + assert(angle > 0); double r = std::abs(radius); - double angle_step = 2. * acos((r - deviation) / r); - size_t num_steps = size_t(ceil(angle / angle_step)); + size_t num_steps = arc_discretization_steps(r, angle, deviation); + double angle_step = angle / num_steps; Points out; out.reserve(num_steps + 1); diff --git a/src/libslic3r/Geometry/ArcWelder.hpp b/src/libslic3r/Geometry/ArcWelder.hpp index b328c12786..dfc6d886e6 100644 --- a/src/libslic3r/Geometry/ArcWelder.hpp +++ b/src/libslic3r/Geometry/ArcWelder.hpp @@ -54,8 +54,10 @@ inline typename Derived::Scalar arc_angle( using Float = typename Derived::Scalar; Float a = Float(0.5) * (end_pos - start_pos).norm() / radius; return radius > Float(0) ? - (a > Float( 1.) ? Float( M_PI) : Float(2.) * std::asin(a)) : - (a < Float(-1.) ? Float( - M_PI) : Float(2. * M_PI) + Float(2.) * std::asin(a)); + // acute angle: + (a > Float( 1.) ? Float(M_PI) : Float(2.) * std::asin(a)) : + // obtuse angle: + (a < Float(-1.) ? Float(M_PI) : Float(2. * M_PI) + Float(2.) * std::asin(a)); } // Calculate positive length of an arc given two points and a radius. @@ -164,6 +166,30 @@ inline bool inside_arc_wedge( radius > 0, ccw, query_pt); } +// Return number of linear segments necessary to interpolate arc of a given positive radius and positive angle to satisfy +// maximum deviation of an interpolating polyline from an analytic arc. +template +size_t arc_discretization_steps(const FloatType radius, const FloatType angle, const FloatType deviation) +{ + assert(radius > 0); + assert(angle > 0); + assert(angle <= FloatType(2. * M_PI)); + assert(deviation > 0); + + FloatType d = radius - deviation; + return d < EPSILON ? + // Radius smaller than deviation. + ( // Acute angle: a single segment interpolates the arc with sufficient accuracy. + angle < M_PI || + // Obtuse angle: Test whether the furthest point (center) of an arc is closer than deviation to the center of a line segment. + radius * (FloatType(1.) + cos(M_PI - FloatType(.5) * angle)) < deviation ? + // Single segment is sufficient + 1 : + // Two segments are necessary, the middle point is at the center of the arc. + 2) : + size_t(ceil(angle / (2. * acos(d / radius)))); +} + // Discretize arc given the radius, orientation and maximum deviation from the arc. // Returned polygon starts with p1, ends with p2 and it is discretized to guarantee the maximum deviation. Points arc_discretize(const Point &p1, const Point &p2, const double radius, const bool ccw, const double deviation);