ArcWelder: Improved resolution of arc discretization in G-code preview

This commit is contained in:
Vojtech Bubnik 2023-07-26 11:24:32 +02:00
parent 95641d0269
commit 98c011d59b
8 changed files with 79 additions and 23 deletions

View File

@ -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<double>(), p.cast<double>(), 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);

View File

@ -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<std::string> 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<std::string> 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<double> g1_feedrate = std::nullopt;
if (line.has_f()) g1_feedrate = (double)line.f();
std::optional<std::string> 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<std::optional<double>, 4>& axes, std::optional<double> feedrate, std::optional<std::string> cmt)
void GCodeProcessor::process_G1(const std::array<std::optional<double>, 4>& axes, std::optional<double> feedrate, G1DiscretizationOrigin origin)
{
const float filament_diameter = (static_cast<size_t>(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<std::optional<double>, 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<std::optional<double>, 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<std::optional<double>, 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<std::string> 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<size_t>(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;

View File

@ -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<std::optional<double>, 4>& axes = { std::nullopt, std::nullopt, std::nullopt, std::nullopt },
std::optional<double> feedrate = std::nullopt, std::optional<std::string> cmt = std::nullopt);
std::optional<double> feedrate = std::nullopt, G1DiscretizationOrigin origin = G1DiscretizationOrigin::G1);
// Arc Move
void process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise);

View File

@ -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);

View File

@ -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,

View File

@ -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<double>(), p_quantized.cast<double>(), double(radius), ccw);
float angle = Geometry::ArcWelder::arc_angle(prev_quantized.cast<double>(), p_quantized.cast<double>(), 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 {

View File

@ -41,10 +41,11 @@ Points arc_discretize(const Point &p1, const Point &p2, const double radius, con
{
Vec2d center = arc_center(p1.cast<double>(), p2.cast<double>(), radius, ccw);
double angle = arc_angle(p1.cast<double>(), p2.cast<double>(), 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);

View File

@ -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<typename FloatType>
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);