From 7eca48b7553a48c1714ec3d8b9f748ea57b687ff Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 17 Jul 2023 16:56:08 +0200 Subject: [PATCH] ArcWelder: Extended cooling buffer with G2/G3 IJ/R --- src/libslic3r/GCode/CoolingBuffer.cpp | 108 ++++++++++++++++++-------- src/libslic3r/GCode/CoolingBuffer.hpp | 8 +- src/libslic3r/Geometry/ArcWelder.hpp | 28 ++++++- 3 files changed, 108 insertions(+), 36 deletions(-) diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 9ac4233e00..11974a55f3 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -33,36 +33,45 @@ CoolingBuffer::CoolingBuffer(GCodeGenerator &gcodegen) : m_config(gcodegen.confi void CoolingBuffer::reset(const Vec3d &position) { - m_current_pos.assign(5, 0.f); - m_current_pos[0] = float(position.x()); - m_current_pos[1] = float(position.y()); - m_current_pos[2] = float(position.z()); - m_current_pos[4] = float(m_config.travel_speed.value); + assert(m_current_pos.size() == 5); + m_current_pos[AxisIdx::X] = float(position.x()); + m_current_pos[AxisIdx::Y] = float(position.y()); + m_current_pos[AxisIdx::Z] = float(position.z()); + m_current_pos[AxisIdx::E] = 0.f; + m_current_pos[AxisIdx::F] = float(m_config.travel_speed.value); m_fan_speed = -1; } struct CoolingLine { - enum Type { + enum Type : uint32_t { TYPE_SET_TOOL = 1 << 0, TYPE_EXTRUDE_END = 1 << 1, TYPE_BRIDGE_FAN_START = 1 << 2, TYPE_BRIDGE_FAN_END = 1 << 3, TYPE_G0 = 1 << 4, TYPE_G1 = 1 << 5, - TYPE_ADJUSTABLE = 1 << 6, - TYPE_EXTERNAL_PERIMETER = 1 << 7, + // G2 or G3: Arc interpolation + TYPE_G2G3 = 1 << 6, + TYPE_ADJUSTABLE = 1 << 7, + TYPE_EXTERNAL_PERIMETER = 1 << 8, + // Arc interpolation, counter-clockwise. + TYPE_G2G3_CCW = 1 << 9, + // Arc interpolation, arc defined by IJ (offset of arc center from its start position). + TYPE_G2G3_IJ = 1 << 10, + // Arc interpolation, arc defined by R (arc radius, positive - smaller, negative - larger). + TYPE_G2G3_R = 1 << 11, // The line sets a feedrate. - TYPE_HAS_F = 1 << 8, - TYPE_WIPE = 1 << 9, - TYPE_G4 = 1 << 10, - TYPE_G92 = 1 << 11, + TYPE_HAS_F = 1 << 12, + TYPE_WIPE = 1 << 13, + TYPE_G4 = 1 << 14, + TYPE_G92 = 1 << 15, // Would be TYPE_ADJUSTABLE, but the block of G-code lines has zero extrusion length, thus the block // cannot have its speed adjusted. This should not happen (sic!). - TYPE_ADJUSTABLE_EMPTY = 1 << 12, + TYPE_ADJUSTABLE_EMPTY = 1 << 16, // Custom fan speed (introduced for overhang fan speed) - TYPE_SET_FAN_SPEED = 1 << 13, - TYPE_RESET_FAN_SPEED = 1 << 14, + TYPE_SET_FAN_SPEED = 1 << 17, + TYPE_RESET_FAN_SPEED = 1 << 18, }; CoolingLine(unsigned int type, size_t line_start, size_t line_end) : @@ -324,7 +333,7 @@ std::string CoolingBuffer::process_layer(std::string &&gcode, size_t layer_id, b // Parse the layer G-code for the moves, which could be adjusted. // Return the list of parsed lines, bucketed by an extruder. -std::vector CoolingBuffer::parse_layer_gcode(const std::string &gcode, std::vector ¤t_pos) const +std::vector CoolingBuffer::parse_layer_gcode(const std::string &gcode, std::array ¤t_pos) const { std::vector per_extruder_adjustments(m_extruder_ids.size()); std::vector map_extruder_to_per_extruder_adjustment(m_num_extruders, 0); @@ -347,7 +356,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: // for a sequence of extrusion moves. size_t active_speed_modifier = size_t(-1); - std::vector new_pos; + std::array new_pos; for (; *line_start != 0; line_start = line_end) { while (*line_end != '\n' && *line_end != 0) @@ -362,12 +371,20 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: line.type = CoolingLine::TYPE_G0; else if (boost::starts_with(sline, "G1 ")) line.type = CoolingLine::TYPE_G1; + else if (boost::starts_with(sline, "G2 ")) + // Arc, clockwise. + line.type = CoolingLine::TYPE_G2G3; + else if (boost::starts_with(sline, "G3 ")) + // Arc, counter-clockwise. + line.type = CoolingLine::TYPE_G2G3 | CoolingLine::TYPE_G2G3_CCW; else if (boost::starts_with(sline, "G92 ")) line.type = CoolingLine::TYPE_G92; if (line.type) { - // G0, G1 or G92 + // G0, G1, G2, G3 or G92 + // Initialize current_pos from new_pos, set IJKR to zero. + std::fill(std::copy(std::begin(current_pos), std::end(current_pos), std::begin(new_pos)), + std::end(new_pos), 0.f); // Parse the G-code line. - new_pos = current_pos; for (auto c = sline.begin() + 3;;) { // Skip whitespaces. for (; c != sline.end() && (*c == ' ' || *c == '\t'); ++ c); @@ -376,21 +393,31 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: // Parse the axis. size_t axis = (*c >= 'X' && *c <= 'Z') ? (*c - 'X') : - (*c == extrusion_axis) ? 3 : (*c == 'F') ? 4 : size_t(-1); + (*c == extrusion_axis) ? AxisIdx::E : (*c == 'F') ? AxisIdx::F : + (*c >= 'I' && *c <= 'K') ? int(AxisIdx::I) + (*c - 'I') : + (*c == 'R') ? AxisIdx::R : size_t(-1); if (axis != size_t(-1)) { //auto [pend, ec] = fast_float::from_chars(&*(++ c), sline.data() + sline.size(), new_pos[axis]); - if (axis == 4) { + if (axis == AxisIdx::F) { // Convert mm/min to mm/sec. - new_pos[4] /= 60.f; + new_pos[AxisIdx::F] /= 60.f; if ((line.type & CoolingLine::TYPE_G92) == 0) // This is G0 or G1 line and it sets the feedrate. This mark is used for reducing the duplicate F calls. line.type |= CoolingLine::TYPE_HAS_F; - } + } else if (axis >= AxisIdx::I && axis <= AxisIdx::J) + line.type |= CoolingLine::TYPE_G2G3_IJ; + else if (axis == AxisIdx::R) + line.type |= CoolingLine::TYPE_G2G3_R; } // Skip this word. for (; c != sline.end() && *c != ' ' && *c != '\t'; ++ c); } + // If G2 or G3, then either center of the arc or radius has to be defined. + assert(! (line.type & CoolingLine::TYPE_G2G3) || + (line.type & (CoolingLine::TYPE_G2G3_IJ | CoolingLine::TYPE_G2G3_R))); + // Arc is defined either by IJ or by R, not by both. + assert(! ((line.type & CoolingLine::TYPE_G2G3_IJ) && (line.type & CoolingLine::TYPE_G2G3_R))); bool external_perimeter = boost::contains(sline, ";_EXTERNAL_PERIMETER"); bool wipe = boost::contains(sline, ";_WIPE"); if (external_perimeter) @@ -402,23 +429,40 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: active_speed_modifier = adjustment->lines.size(); } if ((line.type & CoolingLine::TYPE_G92) == 0) { - // G0 or G1. Calculate the duration. + // G0, G1, G2, G3. Calculate the duration. + assert((line.type & CoolingLine::TYPE_G0) + (line.type & CoolingLine::TYPE_G1) + (line.type & CoolingLine::TYPE_G2G3) == 1); if (m_config.use_relative_e_distances.value) // Reset extruder accumulator. - current_pos[3] = 0.f; + current_pos[AxisIdx::E] = 0.f; float dif[4]; for (size_t i = 0; i < 4; ++ i) dif[i] = new_pos[i] - current_pos[i]; - float dxy2 = dif[0] * dif[0] + dif[1] * dif[1]; - float dxyz2 = dxy2 + dif[2] * dif[2]; + float dxy2; + if (line.type & CoolingLine::TYPE_G2G3) { + // Measure arc length. + if (line.type & CoolingLine::TYPE_G2G3_IJ) { + dxy2 = sqr(Geometry::ArcWelder::arc_length( + Vec2d(current_pos[AxisIdx::X], current_pos[AxisIdx::Y]), + Vec2d(new_pos[AxisIdx::X], new_pos[AxisIdx::Y]), + Vec2d(current_pos[AxisIdx::X] + current_pos[AxisIdx::I], current_pos[AxisIdx::Y] + current_pos[AxisIdx::J]), + line.type & CoolingLine::TYPE_G2G3_CCW)); + } else if (line.type & CoolingLine::TYPE_G2G3_R) { + dxy2 = sqr(Geometry::ArcWelder::arc_length( + Vec2d(current_pos[AxisIdx::X], current_pos[AxisIdx::Y]), + Vec2d(new_pos[AxisIdx::X], new_pos[AxisIdx::Y]), + double(new_pos[AxisIdx::R]))); + } + } else + dxy2 = sqr(dif[AxisIdx::X]) + sqr(dif[AxisIdx::Y]); + float dxyz2 = dxy2 + sqr(dif[AxisIdx::Z]); if (dxyz2 > 0.f) { // Movement in xyz, calculate time from the xyz Euclidian distance. line.length = sqrt(dxyz2); - } else if (std::abs(dif[3]) > 0.f) { + } else if (std::abs(dif[AxisIdx::E]) > 0.f) { // Movement in the extruder axis. - line.length = std::abs(dif[3]); + line.length = std::abs(dif[AxisIdx::E]); } - line.feedrate = new_pos[4]; + line.feedrate = new_pos[AxisIdx::F]; assert((line.type & CoolingLine::TYPE_ADJUSTABLE) == 0 || line.feedrate > 0.f); if (line.length > 0) { assert(line.feedrate > 0); @@ -430,7 +474,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: assert(adjustment->min_print_speed >= 0); line.time_max = (adjustment->min_print_speed == 0.f) ? FLT_MAX : std::max(line.time, line.length / adjustment->min_print_speed); } - if (active_speed_modifier < adjustment->lines.size() && (line.type & CoolingLine::TYPE_G1)) { + if (active_speed_modifier < adjustment->lines.size() && (line.type & (CoolingLine::TYPE_G1 | CoolingLine::TYPE_G2G3))) { // Inside the ";_EXTRUDE_SET_SPEED" blocks, there must not be a G1 Fxx entry. assert((line.type & CoolingLine::TYPE_HAS_F) == 0); CoolingLine &sm = adjustment->lines[active_speed_modifier]; @@ -447,7 +491,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: line.type = 0; } } - current_pos = std::move(new_pos); + std::copy(std::begin(new_pos), std::begin(new_pos) + 5, std::begin(current_pos)); } else if (boost::starts_with(sline, ";_EXTRUDE_END")) { // Closing a block of non-zero length extrusion moves. line.type = CoolingLine::TYPE_EXTRUDE_END; diff --git a/src/libslic3r/GCode/CoolingBuffer.hpp b/src/libslic3r/GCode/CoolingBuffer.hpp index b01a8ab985..9950a5f7ef 100644 --- a/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/src/libslic3r/GCode/CoolingBuffer.hpp @@ -31,7 +31,7 @@ public: private: CoolingBuffer& operator=(const CoolingBuffer&) = delete; - std::vector parse_layer_gcode(const std::string &gcode, std::vector ¤t_pos) const; + std::vector parse_layer_gcode(const std::string &gcode, std::array ¤t_pos) const; float calculate_layer_slowdown(std::vector &per_extruder_adjustments); // Apply slow down over G-code lines stored in per_extruder_adjustments, enable fan if needed. // Returns the adjusted G-code. @@ -40,9 +40,11 @@ private: // G-code snippet cached for the support layers preceding an object layer. std::string m_gcode; // Internal data. - // X,Y,Z,E,F std::vector m_axis; - std::vector m_current_pos; + enum AxisIdx : int { + X = 0, Y, Z, E, F, I, J, K, R, Count + }; + std::array m_current_pos; // Current known fan speed or -1 if not known yet. int m_fan_speed; // Cached from GCodeWriter. diff --git a/src/libslic3r/Geometry/ArcWelder.hpp b/src/libslic3r/Geometry/ArcWelder.hpp index dab73b532a..5bf806c702 100644 --- a/src/libslic3r/Geometry/ArcWelder.hpp +++ b/src/libslic3r/Geometry/ArcWelder.hpp @@ -15,7 +15,7 @@ template inline Eigen::Matrix arc_center( const Eigen::MatrixBase &start_pos, const Eigen::MatrixBase &end_pos, - const typename Float radius, + const Float radius, const bool is_ccw) { static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "arc_center(): first parameter is not a 2D vector"); @@ -73,6 +73,32 @@ inline typename Derived::Scalar arc_length( return arc_angle(start_pos, end_pos, radius) * std::abs(radius); } +// Calculate positive length of an arc given two points, center and orientation. +template +inline typename Derived::Scalar arc_length( + const Eigen::MatrixBase &start_pos, + const Eigen::MatrixBase &end_pos, + const Eigen::MatrixBase ¢er_pos, + const bool ccw) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "arc_length(): first parameter is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "arc_length(): second parameter is not a 2D vector"); + static_assert(Derived3::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "arc_length(): third parameter is not a 2D vector"); + static_assert(std::is_same::value && + std::is_same::value, "arc_length(): All third points must be of the same type."); + using Float = typename Derived::Scalar; + auto vstart = start_pos - center_pos; + auto vend = end_pos - center_pos; + Float radius = vstart.norm(); + Float angle = atan2(double(cross2(vstart, vend)), double(vstart.dot(vend))); + if (! ccw) + angle *= Float(-1.); + if (angle < 0) + angle += Float(2. * M_PI); + assert(angle >= Float(0.) && angle < Float(2. * M_PI + EPSILON)); + return angle * radius; +} + // Test whether a point is inside a wedge of an arc. template inline bool inside_arc_wedge_vectors(