ArcWelder: Extended cooling buffer with G2/G3 IJ/R

This commit is contained in:
Vojtech Bubnik 2023-07-17 16:56:08 +02:00
parent 5111a3d4cf
commit 7eca48b755
3 changed files with 108 additions and 36 deletions

View File

@ -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<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::string &gcode, std::vector<float> &current_pos) const
std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::string &gcode, std::array<float, 5> &current_pos) const
{
std::vector<PerExtruderAdjustments> per_extruder_adjustments(m_extruder_ids.size());
std::vector<size_t> map_extruder_to_per_extruder_adjustment(m_num_extruders, 0);
@ -347,7 +356,7 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
// for a sequence of extrusion moves.
size_t active_speed_modifier = size_t(-1);
std::vector<float> new_pos;
std::array<float, AxisIdx::Count> new_pos;
for (; *line_start != 0; line_start = line_end)
{
while (*line_end != '\n' && *line_end != 0)
@ -362,12 +371,20 @@ std::vector<PerExtruderAdjustments> 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<PerExtruderAdjustments> 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<PerExtruderAdjustments> 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<PerExtruderAdjustments> 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<PerExtruderAdjustments> 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;

View File

@ -31,7 +31,7 @@ public:
private:
CoolingBuffer& operator=(const CoolingBuffer&) = delete;
std::vector<PerExtruderAdjustments> parse_layer_gcode(const std::string &gcode, std::vector<float> &current_pos) const;
std::vector<PerExtruderAdjustments> parse_layer_gcode(const std::string &gcode, std::array<float, 5> &current_pos) const;
float calculate_layer_slowdown(std::vector<PerExtruderAdjustments> &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<char> m_axis;
std::vector<float> m_current_pos;
enum AxisIdx : int {
X = 0, Y, Z, E, F, I, J, K, R, Count
};
std::array<float, 5> m_current_pos;
// Current known fan speed or -1 if not known yet.
int m_fan_speed;
// Cached from GCodeWriter.

View File

@ -15,7 +15,7 @@ template<typename Derived, typename Derived2, typename Float>
inline Eigen::Matrix<Float, 2, 1, Eigen::DontAlign> arc_center(
const Eigen::MatrixBase<Derived> &start_pos,
const Eigen::MatrixBase<Derived2> &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<typename Derived, typename Derived2, typename Derived3>
inline typename Derived::Scalar arc_length(
const Eigen::MatrixBase<Derived> &start_pos,
const Eigen::MatrixBase<Derived2> &end_pos,
const Eigen::MatrixBase<Derived3> &center_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<typename Derived::Scalar, typename Derived2::Scalar>::value &&
std::is_same<typename Derived::Scalar, typename Derived3::Scalar>::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<typename Derived, typename Derived2, typename Derived3>
inline bool inside_arc_wedge_vectors(