ArcWelder: Bugfixes & switchable G2/3 R vs. IJ

This commit is contained in:
Vojtech Bubnik 2023-07-14 16:22:03 +02:00
parent 3df8da662e
commit 7551b4ffd3
8 changed files with 83 additions and 23 deletions

View File

@ -1263,7 +1263,7 @@ void GCodeGenerator::process_layers(
size_t layer_to_print_idx = 0; size_t layer_to_print_idx = 0;
const GCode::SmoothPathCache::InterpolationParameters interpolation_params { const GCode::SmoothPathCache::InterpolationParameters interpolation_params {
scaled<double>(print.config().gcode_resolution.value), scaled<double>(print.config().gcode_resolution.value),
print.config().arc_fitting && ! print.config().spiral_vase ? print.config().arc_fitting != ArcFittingType::Disabled && ! print.config().spiral_vase ?
Geometry::ArcWelder::default_arc_length_percent_tolerance : Geometry::ArcWelder::default_arc_length_percent_tolerance :
0 0
}; };
@ -1362,7 +1362,7 @@ void GCodeGenerator::process_layers(
size_t layer_to_print_idx = 0; size_t layer_to_print_idx = 0;
const GCode::SmoothPathCache::InterpolationParameters interpolation_params { const GCode::SmoothPathCache::InterpolationParameters interpolation_params {
scaled<double>(print.config().gcode_resolution.value), scaled<double>(print.config().gcode_resolution.value),
print.config().arc_fitting && ! print.config().spiral_vase ? print.config().arc_fitting != ArcFittingType::Disabled && ! print.config().spiral_vase ?
Geometry::ArcWelder::default_arc_length_percent_tolerance : Geometry::ArcWelder::default_arc_length_percent_tolerance :
0 0
}; };
@ -2828,6 +2828,7 @@ std::string GCodeGenerator::_extrude(
Vec2d prev = this->point_to_gcode_quantized(path.front().point); Vec2d prev = this->point_to_gcode_quantized(path.front().point);
auto it = path.begin(); auto it = path.begin();
auto end = path.end(); auto end = path.end();
const bool emit_radius = m_config.arc_fitting == ArcFittingType::EmitRadius;
for (++ it; it != end; ++ it) { for (++ it; it != end; ++ it) {
Vec2d p = this->point_to_gcode_quantized(it->point); Vec2d p = this->point_to_gcode_quantized(it->point);
if (it->radius == 0) { if (it->radius == 0) {
@ -2837,12 +2838,25 @@ std::string GCodeGenerator::_extrude(
gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment); gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment);
} else { } else {
// Extrude an arc. // Extrude an arc.
double radius = GCodeFormatter::quantize_xyzf(it->radius); assert(m_config.arc_fitting == ArcFittingType::EmitCenter ||
Vec2f center = Geometry::ArcWelder::arc_center(prev.cast<float>(), p.cast<float>(), float(radius), it->ccw()); m_config.arc_fitting == ArcFittingType::EmitRadius);
float angle = Geometry::ArcWelder::arc_angle(prev.cast<float>(), p.cast<float>(), float(radius)); double radius = unscaled<double>(it->radius);
if (emit_radius)
// Only quantize radius if emitting it directly into G-code. Otherwise use the exact radius for calculating the IJ values.
radius = GCodeFormatter::quantize_xyzf(radius);
Vec2d ij;
if (! emit_radius) {
// Calculate quantized IJ circle center offset.
Vec2d center_raw = Geometry::ArcWelder::arc_center(prev.cast<double>(), p.cast<double>(), double(radius), it->ccw()) - prev;
ij = Vec2d{ GCodeFormatter::quantize_xyzf(center_raw.x()), GCodeFormatter::quantize_xyzf(center_raw.y()) };
}
double angle = Geometry::ArcWelder::arc_angle(prev.cast<double>(), p.cast<double>(), double(radius));
const double line_length = angle * std::abs(radius); const double line_length = angle * std::abs(radius);
path_length += line_length; path_length += line_length;
gcode += m_writer.extrude_to_xy_G2G3R(p, radius, it->ccw(), e_per_mm * line_length, comment); const double dE = e_per_mm * line_length;
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);
} }
prev = p; prev = p;
} }

View File

@ -384,9 +384,13 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std:
return w.string(); return w.string();
} }
std::string GCodeWriter::extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, double dE, const bool ccw, const std::string_view comment) std::string GCodeWriter::extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, double dE, const std::string_view comment)
{ {
assert(dE > 0); assert(dE != 0);
assert(std::abs(point.x()) < 1200.);
assert(std::abs(point.y()) < 1200.);
assert(std::abs(ij.x()) < 1200.);
assert(std::abs(ij.y()) < 1200.);
m_pos.head<2>() = point.head<2>(); m_pos.head<2>() = point.head<2>();
GCodeG2G3Formatter w(ccw); GCodeG2G3Formatter w(ccw);
@ -397,9 +401,12 @@ std::string GCodeWriter::extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &i
return w.string(); return w.string();
} }
std::string GCodeWriter::extrude_to_xy_G2G3R(const Vec2d &point, const double radius, double dE, const bool ccw, const std::string_view comment) std::string GCodeWriter::extrude_to_xy_G2G3R(const Vec2d &point, const double radius, const bool ccw, double dE, const std::string_view comment)
{ {
assert(dE > 0); assert(dE != 0);
assert(std::abs(point.x()) < 1200.);
assert(std::abs(point.y()) < 1200.);
assert(std::abs(radius) < 1800.);
m_pos.head<2>() = point.head<2>(); m_pos.head<2>() = point.head<2>();
GCodeG2G3Formatter w(ccw); GCodeG2G3Formatter w(ccw);

View File

@ -66,8 +66,8 @@ public:
std::string travel_to_z(double z, const std::string_view comment = {}); std::string travel_to_z(double z, const std::string_view comment = {});
bool will_move_z(double z) const; bool will_move_z(double z) const;
std::string extrude_to_xy(const Vec2d &point, double dE, const std::string_view comment = {}); std::string extrude_to_xy(const Vec2d &point, double dE, const std::string_view comment = {});
std::string extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, double dE, const bool ccw, const std::string_view comment); std::string extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, double dE, const std::string_view comment);
std::string extrude_to_xy_G2G3R(const Vec2d &point, const double radius, double dE, const bool ccw, const std::string_view comment); std::string extrude_to_xy_G2G3R(const Vec2d &point, const double radius, const bool ccw, double dE, const std::string_view comment);
// std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string_view comment = {}); // std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string_view comment = {});
std::string retract(bool before_wipe = false); std::string retract(bool before_wipe = false);
std::string retract_for_toolchange(bool before_wipe = false); std::string retract_for_toolchange(bool before_wipe = false);

View File

@ -76,6 +76,7 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange)
{ {
std::string gcode; std::string gcode;
const Extruder &extruder = *gcodegen.writer().extruder(); const Extruder &extruder = *gcodegen.writer().extruder();
static constexpr const std::string_view wipe_retract_comment = "wipe and retract"sv;
// Remaining quantized retraction length. // Remaining quantized retraction length.
if (double retract_length = extruder.retract_to_go(toolchange ? extruder.retract_length_toolchange() : extruder.retract_length()); if (double retract_length = extruder.retract_to_go(toolchange ? extruder.retract_length_toolchange() : extruder.retract_length());
@ -103,14 +104,16 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange)
dE = retract_length; dE = retract_length;
done = true; done = true;
} }
gcode += gcodegen.writer().extrude_to_xy(p, -dE, "wipe and retract"sv); gcode += gcodegen.writer().extrude_to_xy(p, -dE, wipe_retract_comment);
retract_length -= dE; retract_length -= dE;
return done; return done;
}; };
auto wipe_arc = [&gcode, &gcodegen, &retract_length, xy_to_e](const Vec2d &prev, Vec2d &p, float radius_in, const bool ccw) { const bool emit_radius = gcodegen.config().arc_fitting == ArcFittingType::EmitRadius;
double radius = GCodeFormatter::quantize_xyzf(radius_in); auto wipe_arc = [&gcode, &gcodegen, &retract_length, xy_to_e, emit_radius](const Vec2d &prev, Vec2d &p, double radius_in, const bool ccw) {
Vec2f center = Geometry::ArcWelder::arc_center(prev.cast<float>(), p.cast<float>(), float(radius), ccw); // Only quantize radius if emitting it directly into G-code. Otherwise use the exact radius for calculating the IJ values.
float angle = Geometry::ArcWelder::arc_angle(prev.cast<float>(), p.cast<float>(), float(radius)); double radius = emit_radius ? GCodeFormatter::quantize_xyzf(radius_in) : radius_in;
Vec2d center = Geometry::ArcWelder::arc_center(prev.cast<double>(), p.cast<double>(), double(radius), ccw);
float angle = Geometry::ArcWelder::arc_angle(prev.cast<double>(), p.cast<double>(), double(radius));
double segment_length = angle * std::abs(radius); double segment_length = angle * std::abs(radius);
double dE = GCodeFormatter::quantize_e(xy_to_e * segment_length); double dE = GCodeFormatter::quantize_e(xy_to_e * segment_length);
bool done = false; bool done = false;
@ -121,7 +124,13 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange)
dE = retract_length; dE = retract_length;
done = true; done = true;
} }
gcode += gcodegen.writer().extrude_to_xy_G2G3R(p, radius, ccw, -dE, "wipe and retract"sv); if (emit_radius) {
gcode += gcodegen.writer().extrude_to_xy_G2G3R(p, radius, ccw, -dE, wipe_retract_comment);
} else {
// Calculate quantized IJ circle center offset.
Vec2d ij{ GCodeFormatter::quantize_xyzf(center.x() - prev.x()), GCodeFormatter::quantize_xyzf(center.y() - prev.y()) };
gcode += gcodegen.writer().extrude_to_xy_G2G3IJ(p, ij, ccw, -dE, wipe_retract_comment);
}
retract_length -= dE; retract_length -= dE;
return done; return done;
}; };
@ -144,7 +153,7 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange)
start_wipe(); start_wipe();
if (it->linear() ? if (it->linear() ?
wipe_linear(prev, p) : wipe_linear(prev, p) :
wipe_arc(prev, p, it->radius, it->ccw())) wipe_arc(prev, p, unscaled<double>(it->radius), it->ccw()))
break; break;
prev = p; prev = p;
} }

View File

@ -386,6 +386,14 @@ Path fit_path(const Points &src, double tolerance, double fit_circle_percent_tol
out.erase(douglas_peucker_in_place(out.begin() + begin_pl_idx, out.end(), tolerance), out.end()); out.erase(douglas_peucker_in_place(out.begin() + begin_pl_idx, out.end(), tolerance), out.end());
} }
#if 0
// Verify that all the source points are at tolerance distance from the interpolated path.
for (const Point &p : src) {
PathSegmentProjection proj = point_to_path_projection(out, p);
assert(proj.distance2 < sqr(tolerance + SCALED_EPSILON));
}
#endif
return out; return out;
} }

View File

@ -54,7 +54,10 @@ inline Eigen::Matrix<typename Derived::Scalar, 2, 1, Eigen::DontAlign> arc_cente
auto v = end_pos - start_pos; auto v = end_pos - start_pos;
Float q2 = v.squaredNorm(); Float q2 = v.squaredNorm();
assert(q2 > 0); assert(q2 > 0);
Float t = sqrt(sqr(radius) / q2 - Float(.25f)); Float t2 = sqr(radius) / q2 - Float(.25f);
// If the start_pos and end_pos are nearly antipodal, t2 may become slightly negative.
// In that case return a centroid of start_point & end_point.
Float t = t2 > 0 ? sqrt(t2) : Float(0);
auto mid = Float(0.5) * (start_pos + end_pos); auto mid = Float(0.5) * (start_pos + end_pos);
Vector vp{ -v.y() * t, v.x() * t }; Vector vp{ -v.y() * t, v.x() * t };
return (radius > Float(0)) == is_ccw ? (mid + vp).eval() : (mid - vp).eval(); return (radius > Float(0)) == is_ccw ? (mid + vp).eval() : (mid - vp).eval();

View File

@ -34,6 +34,13 @@ static t_config_enum_names enum_names_from_keys_map(const t_config_enum_values &
template<> const t_config_enum_values& ConfigOptionEnum<NAME>::get_enum_values() { return s_keys_map_##NAME; } \ template<> const t_config_enum_values& ConfigOptionEnum<NAME>::get_enum_values() { return s_keys_map_##NAME; } \
template<> const t_config_enum_names& ConfigOptionEnum<NAME>::get_enum_names() { return s_keys_names_##NAME; } template<> const t_config_enum_names& ConfigOptionEnum<NAME>::get_enum_names() { return s_keys_names_##NAME; }
static const t_config_enum_values s_keys_map_ArcFittingType {
{ "disabled", int(ArcFittingType::Disabled) },
{ "emit_center", int(ArcFittingType::EmitCenter) },
{ "emit_radius", int(ArcFittingType::EmitRadius) }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ArcFittingType)
static t_config_enum_values s_keys_map_PrinterTechnology { static t_config_enum_values s_keys_map_PrinterTechnology {
{ "FFF", ptFFF }, { "FFF", ptFFF },
{ "SLA", ptSLA } { "SLA", ptSLA }
@ -397,12 +404,17 @@ void PrintConfigDef::init_fff_params()
{ {
ConfigOptionDef* def; ConfigOptionDef* def;
def = this->add("arc_fitting", coBool); def = this->add("arc_fitting", coEnum);
def->label = L("Arc fitting"); def->label = L("Arc fitting");
def->tooltip = L("Enable this to get a G-code file which has G2 and G3 moves. " def->tooltip = L("Enable this to get a G-code file which has G2 and G3 moves. "
"And the fitting tolerance is same with resolution"); "And the fitting tolerance is same with resolution");
def->set_enum<ArcFittingType>({
{ "disabled", "Disabled" },
{ "emit_center", "Enabled: G2/3 I J" },
{ "emit_radius", "Enabled: G2/3 R" }
});
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false)); def->set_default_value(new ConfigOptionEnum<ArcFittingType>(ArcFittingType::Disabled));
def = this->add("arc_fitting_tolerance", coFloatOrPercent); def = this->add("arc_fitting_tolerance", coFloatOrPercent);
def->label = L("Arc fitting tolerance"); def->label = L("Arc fitting tolerance");

View File

@ -31,6 +31,12 @@
namespace Slic3r { namespace Slic3r {
enum class ArcFittingType {
Disabled,
EmitCenter,
EmitRadius,
};
enum GCodeFlavor : unsigned char { enum GCodeFlavor : unsigned char {
gcfRepRapSprinter, gcfRepRapFirmware, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlinLegacy, gcfMarlinFirmware, gcfKlipper, gcfSailfish, gcfMach3, gcfMachinekit, gcfRepRapSprinter, gcfRepRapFirmware, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlinLegacy, gcfMarlinFirmware, gcfKlipper, gcfSailfish, gcfMach3, gcfMachinekit,
gcfSmoothie, gcfNoExtrusion, gcfSmoothie, gcfNoExtrusion,
@ -142,6 +148,7 @@ enum class GCodeThumbnailsFormat {
template<> const t_config_enum_names& ConfigOptionEnum<NAME>::get_enum_names(); \ template<> const t_config_enum_names& ConfigOptionEnum<NAME>::get_enum_names(); \
template<> const t_config_enum_values& ConfigOptionEnum<NAME>::get_enum_values(); template<> const t_config_enum_values& ConfigOptionEnum<NAME>::get_enum_values();
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ArcFittingType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PrinterTechnology) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PrinterTechnology)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeFlavor) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeFlavor)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(MachineLimitsUsage) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(MachineLimitsUsage)
@ -662,7 +669,7 @@ PRINT_CONFIG_CLASS_DEFINE(
PRINT_CONFIG_CLASS_DEFINE( PRINT_CONFIG_CLASS_DEFINE(
GCodeConfig, GCodeConfig,
((ConfigOptionBool, arc_fitting)) ((ConfigOptionEnum<ArcFittingType>, arc_fitting))
((ConfigOptionFloatOrPercent, arc_fitting_tolerance)) ((ConfigOptionFloatOrPercent, arc_fitting_tolerance))
((ConfigOptionBool, autoemit_temperature_commands)) ((ConfigOptionBool, autoemit_temperature_commands))
((ConfigOptionString, before_layer_gcode)) ((ConfigOptionString, before_layer_gcode))