mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2026-01-30 12:50:50 -07:00
Import PrusaSlicer G2/G3 arc discretization code
This commit is contained in:
parent
20f132e09a
commit
1394a3ccf2
7 changed files with 513 additions and 482 deletions
|
|
@ -246,6 +246,8 @@ set(lisbslic3r_sources
|
|||
GCode/WipeTower.hpp
|
||||
GCodeWriter.cpp
|
||||
GCodeWriter.hpp
|
||||
Geometry/ArcWelder.hpp
|
||||
Geometry/ArcWelder.cpp
|
||||
Geometry/Bicubic.hpp
|
||||
Geometry/Circle.cpp
|
||||
Geometry/Circle.hpp
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
|
||||
#include <chrono>
|
||||
|
||||
#include "Geometry/ArcWelder.hpp"
|
||||
|
||||
static const float DEFAULT_TOOLPATH_WIDTH = 0.4f;
|
||||
static const float DEFAULT_TOOLPATH_HEIGHT = 0.2f;
|
||||
|
||||
|
|
@ -1174,9 +1176,6 @@ void GCodeProcessor::reset()
|
|||
m_flushing = false;
|
||||
m_wipe_tower = false;
|
||||
m_remaining_volume = 0.f;
|
||||
// BBS: arc move related data
|
||||
m_move_path_type = EMovePathType::Noop_move;
|
||||
m_arc_center = Vec3f::Zero();
|
||||
|
||||
m_line_id = 0;
|
||||
m_last_line_id = 0;
|
||||
|
|
@ -1559,8 +1558,8 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool
|
|||
switch (cmd[1]) {
|
||||
case '0': { process_G0(line); break; } // Move
|
||||
case '1': { process_G1(line); break; } // Move
|
||||
case '2':
|
||||
case '3': { process_G2_G3(line); break; } // Move
|
||||
case '2': { process_G2_G3(line, true); break; } // CW Arc Move
|
||||
case '3': { process_G2_G3(line, false); break; } // CCW Arc Move
|
||||
//BBS
|
||||
case 4: { process_G4(line); break; } // Delay
|
||||
default: break;
|
||||
|
|
@ -2549,43 +2548,58 @@ void GCodeProcessor::process_G0(const GCodeReader::GCodeLine& line)
|
|||
}
|
||||
|
||||
void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::optional<unsigned int>& remaining_internal_g1_lines)
|
||||
{
|
||||
std::array<std::optional<double>, 4> g1_axes = { std::nullopt, std::nullopt, std::nullopt, std::nullopt };
|
||||
if (line.has_x()) g1_axes[X] = (double)line.x();
|
||||
if (line.has_y()) g1_axes[Y] = (double)line.y();
|
||||
if (line.has_z()) g1_axes[Z] = (double)line.z();
|
||||
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();
|
||||
process_G1(g1_axes, g1_feedrate);
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_G1(const std::array<std::optional<double>, 4>& axes, const std::optional<double>& feedrate,
|
||||
G1DiscretizationOrigin origin, const std::optional<unsigned int>& remaining_internal_g1_lines)
|
||||
{
|
||||
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();
|
||||
float filament_radius = 0.5f * filament_diameter;
|
||||
float area_filament_cross_section = static_cast<float>(M_PI) * sqr(filament_radius);
|
||||
auto absolute_position = [this, area_filament_cross_section](Axis axis, const GCodeReader::GCodeLine& lineG1) {
|
||||
bool is_relative = (m_global_positioning_type == EPositioningType::Relative);
|
||||
if (axis == E)
|
||||
is_relative |= (m_e_local_positioning_type == EPositioningType::Relative);
|
||||
|
||||
if (lineG1.has(Slic3r::Axis(axis))) {
|
||||
float lengthsScaleFactor = (m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f;
|
||||
float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor;
|
||||
auto move_type = [this](const AxisCoords& delta_pos) {
|
||||
if (m_wiping)
|
||||
return EMoveType::Wipe;
|
||||
else if (delta_pos[E] < 0.0f)
|
||||
return (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) ? EMoveType::Travel : EMoveType::Retract;
|
||||
else if (delta_pos[E] > 0.0f) {
|
||||
if (delta_pos[X] == 0.0f && delta_pos[Y] == 0.0f)
|
||||
return (delta_pos[Z] == 0.0f) ? EMoveType::Unretract : EMoveType::Travel;
|
||||
else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f)
|
||||
return EMoveType::Extrude;
|
||||
}
|
||||
else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f)
|
||||
return EMoveType::Travel;
|
||||
|
||||
return EMoveType::Noop;
|
||||
};
|
||||
|
||||
auto extract_absolute_position_on_axis = [&](Axis axis, std::optional<double> value, double area_filament_cross_section)
|
||||
{
|
||||
if (value.has_value()) {
|
||||
bool is_relative = (m_global_positioning_type == EPositioningType::Relative);
|
||||
if (axis == E)
|
||||
is_relative |= (m_e_local_positioning_type == EPositioningType::Relative);
|
||||
|
||||
const double lengthsScaleFactor = (m_units == EUnits::Inches) ? double(INCHES_TO_MM) : 1.0;
|
||||
double ret = *value * lengthsScaleFactor;
|
||||
// if (axis == E && m_use_volumetric_e)
|
||||
// ret /= area_filament_cross_section;
|
||||
return is_relative ? m_start_position[axis] + ret : m_origin[axis] + ret;
|
||||
}
|
||||
else
|
||||
return m_start_position[axis];
|
||||
};
|
||||
|
||||
auto move_type = [this](const AxisCoords& delta_pos) {
|
||||
EMoveType type = EMoveType::Noop;
|
||||
|
||||
if (m_wiping)
|
||||
type = EMoveType::Wipe;
|
||||
else if (delta_pos[E] < 0.0f)
|
||||
type = (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) ? EMoveType::Travel : EMoveType::Retract;
|
||||
else if (delta_pos[E] > 0.0f) {
|
||||
if (delta_pos[X] == 0.0f && delta_pos[Y] == 0.0f)
|
||||
type = (delta_pos[Z] == 0.0f) ? EMoveType::Unretract : EMoveType::Travel;
|
||||
else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f)
|
||||
type = EMoveType::Extrude;
|
||||
}
|
||||
else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f)
|
||||
type = EMoveType::Travel;
|
||||
|
||||
return type;
|
||||
};
|
||||
|
||||
++m_g1_line_id;
|
||||
|
||||
// enable processing of lines M201/M203/M204/M205
|
||||
|
|
@ -2593,12 +2607,12 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o
|
|||
|
||||
// updates axes positions from line
|
||||
for (unsigned char a = X; a <= E; ++a) {
|
||||
m_end_position[a] = absolute_position((Axis)a, line);
|
||||
m_end_position[a] = extract_absolute_position_on_axis((Axis)a, axes[a], double(area_filament_cross_section));
|
||||
}
|
||||
|
||||
// updates feedrate from line, if present
|
||||
if (line.has_f())
|
||||
m_feedrate = line.f() * MMMIN_TO_MMSEC;
|
||||
if (feedrate.has_value())
|
||||
m_feedrate = (*feedrate) * MMMIN_TO_MMSEC;
|
||||
|
||||
// calculates movement deltas
|
||||
float max_abs_delta = 0.0f;
|
||||
|
|
@ -2633,7 +2647,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o
|
|||
|
||||
if (m_forced_height > 0.0f)
|
||||
m_height = m_forced_height;
|
||||
else {
|
||||
else if (origin == G1DiscretizationOrigin::G1) {
|
||||
if (m_end_position[Z] > m_extruded_last_z + EPSILON)
|
||||
m_height = m_end_position[Z] - m_extruded_last_z;
|
||||
}
|
||||
|
|
@ -2644,7 +2658,8 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o
|
|||
if (m_end_position[Z] == 0.0f)
|
||||
m_end_position[Z] = m_height;
|
||||
|
||||
m_extruded_last_z = m_end_position[Z];
|
||||
if (origin == G1DiscretizationOrigin::G1)
|
||||
m_extruded_last_z = m_end_position[Z];
|
||||
m_options_z_corrector.update(m_height);
|
||||
|
||||
if (m_forced_width > 0.0f)
|
||||
|
|
@ -2952,440 +2967,302 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o
|
|||
store_move_vertex(type);
|
||||
}
|
||||
|
||||
// BBS: this function is absolutely new for G2 and G3 gcode
|
||||
void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
|
||||
void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise)
|
||||
{
|
||||
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();
|
||||
float filament_radius = 0.5f * filament_diameter;
|
||||
float area_filament_cross_section = static_cast<float>(M_PI) * sqr(filament_radius);
|
||||
auto absolute_position = [this, area_filament_cross_section](Axis axis, const GCodeReader::GCodeLine& lineG2_3) {
|
||||
bool is_relative = (m_global_positioning_type == EPositioningType::Relative);
|
||||
if (axis == E)
|
||||
is_relative |= (m_e_local_positioning_type == EPositioningType::Relative);
|
||||
enum class EFitting { None, IJ, R };
|
||||
std::string_view axis_pos_I;
|
||||
std::string_view axis_pos_J;
|
||||
EFitting fitting = EFitting::None;
|
||||
if (line.has('R')) {
|
||||
fitting = EFitting::R;
|
||||
} else {
|
||||
axis_pos_I = line.axis_pos('I');
|
||||
axis_pos_J = line.axis_pos('J');
|
||||
if (! axis_pos_I.empty() || ! axis_pos_J.empty())
|
||||
fitting = EFitting::IJ;
|
||||
}
|
||||
|
||||
if (lineG2_3.has(Slic3r::Axis(axis))) {
|
||||
float lengthsScaleFactor = (m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f;
|
||||
float ret = lineG2_3.value(Slic3r::Axis(axis)) * lengthsScaleFactor;
|
||||
if (axis == I)
|
||||
return m_start_position[X] + ret;
|
||||
else if (axis == J)
|
||||
return m_start_position[Y] + ret;
|
||||
else
|
||||
return is_relative ? m_start_position[axis] + ret : m_origin[axis] + ret;
|
||||
}
|
||||
else {
|
||||
if (axis == I)
|
||||
return m_start_position[X];
|
||||
else if (axis == J)
|
||||
return m_start_position[Y];
|
||||
else
|
||||
return m_start_position[axis];
|
||||
}
|
||||
if (fitting == EFitting::None)
|
||||
return;
|
||||
|
||||
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;
|
||||
const float area_filament_cross_section = static_cast<float>(M_PI) * sqr(filament_radius);
|
||||
|
||||
AxisCoords end_position = m_start_position;
|
||||
for (unsigned char a = X; a <= E; ++a) {
|
||||
end_position[a] = extract_absolute_position_on_axis((Axis)a, line, double(area_filament_cross_section));
|
||||
}
|
||||
|
||||
// relative center
|
||||
Vec3f rel_center = Vec3f::Zero();
|
||||
#ifndef NDEBUG
|
||||
double radius = 0.0;
|
||||
#endif // NDEBUG
|
||||
if (fitting == EFitting::R) {
|
||||
float r;
|
||||
if (!line.has_value('R', r) || r == 0.0f)
|
||||
return;
|
||||
#ifndef NDEBUG
|
||||
radius = (double)std::abs(r);
|
||||
#endif // NDEBUG
|
||||
const Vec2f start_pos((float)m_start_position[X], (float)m_start_position[Y]);
|
||||
const Vec2f end_pos((float)end_position[X], (float)end_position[Y]);
|
||||
const Vec2f c = Geometry::ArcWelder::arc_center(start_pos, end_pos, r, !clockwise);
|
||||
rel_center.x() = c.x() - m_start_position[X];
|
||||
rel_center.y() = c.y() - m_start_position[Y];
|
||||
}
|
||||
else {
|
||||
assert(fitting == EFitting::IJ);
|
||||
if (! axis_pos_I.empty() && ! line.has_value(axis_pos_I, rel_center.x()))
|
||||
return;
|
||||
if (! axis_pos_J.empty() && ! line.has_value(axis_pos_J, rel_center.y()))
|
||||
return;
|
||||
}
|
||||
|
||||
// scale center, if needed
|
||||
if (m_units == EUnits::Inches)
|
||||
rel_center *= INCHES_TO_MM;
|
||||
|
||||
struct Arc
|
||||
{
|
||||
Vec3d start{ Vec3d::Zero() };
|
||||
Vec3d end{ Vec3d::Zero() };
|
||||
Vec3d center{ Vec3d::Zero() };
|
||||
|
||||
double angle{ 0.0 };
|
||||
double delta_x() const { return end.x() - start.x(); }
|
||||
double delta_y() const { return end.y() - start.y(); }
|
||||
double delta_z() const { return end.z() - start.z(); }
|
||||
|
||||
double length() const { return angle * start_radius(); }
|
||||
double travel_length() const { return std::sqrt(sqr(length()) + sqr(delta_z())); }
|
||||
double start_radius() const { return (start - center).norm(); }
|
||||
double end_radius() const { return (end - center).norm(); }
|
||||
|
||||
Vec3d relative_start() const { return start - center; }
|
||||
Vec3d relative_end() const { return end - center; }
|
||||
|
||||
bool is_full_circle() const { return std::abs(delta_x()) < EPSILON && std::abs(delta_y()) < EPSILON; }
|
||||
};
|
||||
|
||||
auto move_type = [this](const float& delta_E) {
|
||||
if (delta_E == 0.0f)
|
||||
return EMoveType::Travel;
|
||||
else
|
||||
return EMoveType::Extrude;
|
||||
Arc arc;
|
||||
|
||||
// arc start endpoint
|
||||
arc.start = Vec3d(m_start_position[X], m_start_position[Y], m_start_position[Z]);
|
||||
|
||||
// arc center
|
||||
arc.center = arc.start + rel_center.cast<double>();
|
||||
|
||||
// arc end endpoint
|
||||
arc.end = Vec3d(end_position[X], end_position[Y], end_position[Z]);
|
||||
|
||||
// radii
|
||||
if (std::abs(arc.end_radius() - arc.start_radius()) > 0.001) {
|
||||
// what to do ???
|
||||
}
|
||||
|
||||
assert(fitting != EFitting::R || std::abs(radius - arc.start_radius()) < EPSILON);
|
||||
|
||||
// updates feedrate from line
|
||||
std::optional<float> feedrate;
|
||||
if (line.has_f()) {
|
||||
// feedrate = m_feed_multiply.current * line.f() * MMMIN_TO_MMSEC;
|
||||
feedrate = 1.0f * line.f() * MMMIN_TO_MMSEC;
|
||||
}
|
||||
|
||||
// updates extrusion from line
|
||||
std::optional<float> extrusion;
|
||||
if (line.has_e())
|
||||
extrusion = end_position[E] - m_start_position[E];
|
||||
|
||||
// relative arc endpoints
|
||||
const Vec3d rel_arc_start = arc.relative_start();
|
||||
const Vec3d rel_arc_end = arc.relative_end();
|
||||
|
||||
// arc angle
|
||||
if (arc.is_full_circle())
|
||||
arc.angle = 2.0 * PI;
|
||||
else {
|
||||
arc.angle = std::atan2(rel_arc_start.x() * rel_arc_end.y() - rel_arc_start.y() * rel_arc_end.x(),
|
||||
rel_arc_start.x() * rel_arc_end.x() + rel_arc_start.y() * rel_arc_end.y());
|
||||
if (arc.angle < 0.0)
|
||||
arc.angle += 2.0 * PI;
|
||||
if (clockwise)
|
||||
arc.angle -= 2.0 * PI;
|
||||
}
|
||||
|
||||
const double travel_length = arc.travel_length();
|
||||
if (travel_length < 0.001)
|
||||
return;
|
||||
|
||||
auto adjust_target = [this, area_filament_cross_section](const AxisCoords& target, const AxisCoords& prev_position) {
|
||||
AxisCoords ret = target;
|
||||
if (m_global_positioning_type == EPositioningType::Relative) {
|
||||
for (unsigned char a = X; a <= E; ++a) {
|
||||
ret[a] -= prev_position[a];
|
||||
}
|
||||
}
|
||||
else if (m_e_local_positioning_type == EPositioningType::Relative)
|
||||
ret[E] -= prev_position[E];
|
||||
|
||||
// if (m_use_volumetric_e)
|
||||
// ret[E] *= area_filament_cross_section;
|
||||
|
||||
const double lengthsScaleFactor = (m_units == EUnits::Inches) ? double(INCHES_TO_MM) : 1.0;
|
||||
for (unsigned char a = X; a <= E; ++a) {
|
||||
ret[a] /= lengthsScaleFactor;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
auto arc_interpolation = [this](const Vec3f& start_pos, const Vec3f& end_pos, const Vec3f& center_pos, const bool is_ccw) {
|
||||
float radius = ArcSegment::calc_arc_radius(start_pos, center_pos);
|
||||
//BBS: radius is too small to draw
|
||||
if (radius <= DRAW_ARC_TOLERANCE) {
|
||||
m_interpolation_points.resize(0);
|
||||
return;
|
||||
}
|
||||
float radian_step = 2 * acos((radius - DRAW_ARC_TOLERANCE) / radius);
|
||||
float num = ArcSegment::calc_arc_radian(start_pos, end_pos, center_pos, is_ccw) / radian_step;
|
||||
float z_step = (num < 1)? end_pos.z() - start_pos.z() : (end_pos.z() - start_pos.z()) / num;
|
||||
radian_step = is_ccw ? radian_step : -radian_step;
|
||||
int interpolation_num = floor(num);
|
||||
auto internal_only_g1_line = [this](const AxisCoords& target, bool has_z, const std::optional<float>& feedrate,
|
||||
const std::optional<float>& extrusion, const std::optional<unsigned int>& remaining_internal_g1_lines = std::nullopt) {
|
||||
std::array<std::optional<double>, 4> g1_axes = { target[X], target[Y], std::nullopt, std::nullopt };
|
||||
std::optional<double> g1_feedrate = std::nullopt;
|
||||
if (has_z)
|
||||
g1_axes[Z] = target[Z];
|
||||
if (extrusion.has_value())
|
||||
g1_axes[E] = target[E];
|
||||
if (feedrate.has_value())
|
||||
g1_feedrate = (double)*feedrate;
|
||||
process_G1(g1_axes, g1_feedrate, G1DiscretizationOrigin::G2G3, remaining_internal_g1_lines);
|
||||
};
|
||||
|
||||
m_interpolation_points.resize(interpolation_num, Vec3f::Zero());
|
||||
Vec3f delta = start_pos - center_pos;
|
||||
for (auto i = 0; i < interpolation_num; i++) {
|
||||
float cos_val = cos((i+1) * radian_step);
|
||||
float sin_val = sin((i+1) * radian_step);
|
||||
m_interpolation_points[i] = Vec3f(center_pos.x() + delta.x() * cos_val - delta.y() * sin_val,
|
||||
center_pos.y() + delta.x() * sin_val + delta.y() * cos_val,
|
||||
start_pos.z() + (i + 1) * z_step);
|
||||
}
|
||||
};
|
||||
if (m_flavor == gcfMarlinFirmware) {
|
||||
// calculate arc segments
|
||||
// reference:
|
||||
// Prusa-Firmware-Buddy\lib\Marlin\Marlin\src\gcode\motion\G2_G3.cpp - plan_arc()
|
||||
// https://github.com/prusa3d/Prusa-Firmware-Buddy-Private/blob/private/lib/Marlin/Marlin/src/gcode/motion/G2_G3.cpp
|
||||
|
||||
++m_g1_line_id;
|
||||
static const float MAX_ARC_DEVIATION = 0.02f;
|
||||
static const float MIN_ARC_SEGMENTS_PER_SEC = 50;
|
||||
static const float MIN_ARC_SEGMENT_MM = 0.1f;
|
||||
static const float MAX_ARC_SEGMENT_MM = 2.0f;
|
||||
const float feedrate_mm_s = feedrate.has_value() ? *feedrate : m_feedrate;
|
||||
const float radius_mm = rel_center.norm();
|
||||
const float segment_mm = std::clamp(std::min(std::sqrt(8.0f * radius_mm * MAX_ARC_DEVIATION), feedrate_mm_s * (1.0f / MIN_ARC_SEGMENTS_PER_SEC)), MIN_ARC_SEGMENT_MM, MAX_ARC_SEGMENT_MM);
|
||||
const float flat_mm = radius_mm * std::abs(arc.angle);
|
||||
const size_t segments = std::max<size_t>(flat_mm / segment_mm + 0.8f, 1);
|
||||
|
||||
//BBS: enable processing of lines M201/M203/M204/M205
|
||||
m_time_processor.machine_envelope_processing_enabled = true;
|
||||
AxisCoords prev_target = m_start_position;
|
||||
|
||||
//BBS: get axes positions from line
|
||||
for (unsigned char a = X; a <= E; ++a) {
|
||||
m_end_position[a] = absolute_position((Axis)a, line);
|
||||
}
|
||||
//BBS: G2 G3 line but has no I and J axis, invalid G code format
|
||||
if (!line.has(I) && !line.has(J))
|
||||
return;
|
||||
//BBS: P mode, but xy position is not same, or P is not 1, invalid G code format
|
||||
if (line.has(P) &&
|
||||
(m_start_position[X] != m_end_position[X] ||
|
||||
m_start_position[Y] != m_end_position[Y] ||
|
||||
((int)line.p()) != 1))
|
||||
return;
|
||||
if (segments > 1) {
|
||||
const float inv_segments = 1.0f / static_cast<float>(segments);
|
||||
const float theta_per_segment = static_cast<float>(arc.angle) * inv_segments;
|
||||
const float cos_T = cos(theta_per_segment);
|
||||
const float sin_T = sin(theta_per_segment);
|
||||
const float z_per_segment = arc.delta_z() * inv_segments;
|
||||
const float extruder_per_segment = (extrusion.has_value()) ? *extrusion * inv_segments : 0.0f;
|
||||
|
||||
m_arc_center = Vec3f(absolute_position(I, line),absolute_position(J, line),m_start_position[Z]);
|
||||
//BBS: G2 is CW direction, G3 is CCW direction
|
||||
const std::string_view cmd = line.cmd();
|
||||
m_move_path_type = (::atoi(&cmd[1]) == 2) ? EMovePathType::Arc_move_cw : EMovePathType::Arc_move_ccw;
|
||||
//BBS: get arc length,interpolation points and radian in X-Y plane
|
||||
Vec3f start_point = Vec3f(m_start_position[X], m_start_position[Y], m_start_position[Z]);
|
||||
Vec3f end_point = Vec3f(m_end_position[X], m_end_position[Y], m_end_position[Z]);
|
||||
float arc_length;
|
||||
if (!line.has(P))
|
||||
arc_length = ArcSegment::calc_arc_length(start_point, end_point, m_arc_center, (m_move_path_type == EMovePathType::Arc_move_ccw));
|
||||
else
|
||||
arc_length = ((int)line.p()) * 2 * PI * (start_point - m_arc_center).norm();
|
||||
//BBS: Attention! arc_onterpolation does not support P mode while P is not 1.
|
||||
arc_interpolation(start_point, end_point, m_arc_center, (m_move_path_type == EMovePathType::Arc_move_ccw));
|
||||
float radian = ArcSegment::calc_arc_radian(start_point, end_point, m_arc_center, (m_move_path_type == EMovePathType::Arc_move_ccw));
|
||||
Vec3f start_dir = Circle::calc_tangential_vector(start_point, m_arc_center, (m_move_path_type == EMovePathType::Arc_move_ccw));
|
||||
Vec3f end_dir = Circle::calc_tangential_vector(end_point, m_arc_center, (m_move_path_type == EMovePathType::Arc_move_ccw));
|
||||
static const size_t N_ARC_CORRECTION = 25;
|
||||
size_t arc_recalc_count = N_ARC_CORRECTION;
|
||||
|
||||
//BBS: updates feedrate from line, if present
|
||||
if (line.has_f())
|
||||
m_feedrate = line.f() * MMMIN_TO_MMSEC;
|
||||
|
||||
//BBS: calculates movement deltas
|
||||
AxisCoords delta_pos;
|
||||
for (unsigned char a = X; a <= E; ++a) {
|
||||
delta_pos[a] = m_end_position[a] - m_start_position[a];
|
||||
}
|
||||
|
||||
//BBS: no displacement, return
|
||||
if (arc_length == 0.0f && delta_pos[Z] == 0.0f)
|
||||
return;
|
||||
|
||||
EMoveType type = move_type(delta_pos[E]);
|
||||
|
||||
|
||||
const float delta_xyz = std::sqrt(sqr(arc_length) + sqr(delta_pos[Z]));
|
||||
m_travel_dist = delta_xyz;
|
||||
if (type == EMoveType::Extrude) {
|
||||
float volume_extruded_filament = area_filament_cross_section * delta_pos[E];
|
||||
float area_toolpath_cross_section = volume_extruded_filament / delta_xyz;
|
||||
|
||||
if(m_extrusion_role == ExtrusionRole::erSupportMaterial || m_extrusion_role == ExtrusionRole::erSupportMaterialInterface || m_extrusion_role ==ExtrusionRole::erSupportTransition)
|
||||
m_used_filaments.increase_support_caches(volume_extruded_filament);
|
||||
else if (m_extrusion_role == ExtrusionRole::erWipeTower) {
|
||||
//BBS: save wipe tower volume to the cache
|
||||
m_used_filaments.increase_wipe_tower_caches(volume_extruded_filament);
|
||||
}
|
||||
else {
|
||||
//BBS: save extruded volume to the cache
|
||||
m_used_filaments.increase_model_caches(volume_extruded_filament);
|
||||
}
|
||||
//BBS: volume extruded filament / tool displacement = area toolpath cross section
|
||||
m_mm3_per_mm = area_toolpath_cross_section;
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_mm3_per_mm_compare.update(area_toolpath_cross_section, m_extrusion_role);
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
if (m_forced_height > 0.0f)
|
||||
m_height = m_forced_height;
|
||||
else {
|
||||
if (m_end_position[Z] > m_extruded_last_z + EPSILON)
|
||||
m_height = m_end_position[Z] - m_extruded_last_z;
|
||||
}
|
||||
|
||||
if (m_height == 0.0f)
|
||||
m_height = DEFAULT_TOOLPATH_HEIGHT;
|
||||
|
||||
if (m_end_position[Z] == 0.0f)
|
||||
m_end_position[Z] = m_height;
|
||||
|
||||
m_extruded_last_z = m_end_position[Z];
|
||||
m_options_z_corrector.update(m_height);
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_height_compare.update(m_height, m_extrusion_role);
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
if (m_forced_width > 0.0f)
|
||||
m_width = m_forced_width;
|
||||
else if (m_extrusion_role == erExternalPerimeter)
|
||||
//BBS: cross section: rectangle
|
||||
m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(1.05f * filament_radius)) / (delta_xyz * m_height);
|
||||
else if (m_extrusion_role == erBridgeInfill || m_extrusion_role == erInternalBridgeInfill || m_extrusion_role == erNone)
|
||||
//BBS: cross section: circle
|
||||
m_width = static_cast<float>(m_result.filament_diameters[m_extruder_id]) * std::sqrt(delta_pos[E] / delta_xyz);
|
||||
else
|
||||
//BBS: cross section: rectangle + 2 semicircles
|
||||
m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast<float>(1.0 - 0.25 * M_PI) * m_height;
|
||||
|
||||
if (m_width == 0.0f)
|
||||
m_width = DEFAULT_TOOLPATH_WIDTH;
|
||||
|
||||
//BBS: clamp width to avoid artifacts which may arise from wrong values of m_height
|
||||
m_width = std::min(m_width, std::max(2.0f, 4.0f * m_height));
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_width_compare.update(m_width, m_extrusion_role);
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
}
|
||||
|
||||
//BBS: time estimate section
|
||||
assert(delta_xyz != 0.0f);
|
||||
float inv_distance = 1.0f / delta_xyz;
|
||||
float radius = ArcSegment::calc_arc_radius(start_point, m_arc_center);
|
||||
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
TimeMachine& machine = m_time_processor.machines[i];
|
||||
if (!machine.enabled)
|
||||
continue;
|
||||
|
||||
TimeMachine::State& curr = machine.curr;
|
||||
TimeMachine::State& prev = machine.prev;
|
||||
std::vector<TimeBlock>& blocks = machine.blocks;
|
||||
|
||||
curr.feedrate = (type == EMoveType::Travel) ?
|
||||
minimum_travel_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), m_feedrate) :
|
||||
minimum_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), m_feedrate);
|
||||
|
||||
//BBS: calculeta enter and exit direction
|
||||
curr.enter_direction = start_dir;
|
||||
curr.exit_direction = end_dir;
|
||||
|
||||
TimeBlock block;
|
||||
block.move_type = type;
|
||||
//BBS: don't calculate travel time into extrusion path, except travel inside start and end gcode.
|
||||
block.role = (type != EMoveType::Travel || m_extrusion_role == erCustom) ? m_extrusion_role : erNone;
|
||||
block.distance = delta_xyz;
|
||||
block.g1_line_id = m_g1_line_id;
|
||||
block.layer_id = std::max<unsigned int>(1, m_layer_id);
|
||||
block.flags.prepare_stage = m_processing_start_custom_gcode;
|
||||
|
||||
// BBS: calculates block cruise feedrate
|
||||
// For arc move, we need to limite the cruise according to centripetal acceleration which is
|
||||
// same with acceleration in x-y plane. Because arc move part is only on x-y plane, we use x-y acceleration directly
|
||||
float centripetal_acceleration = get_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i));
|
||||
float max_feedrate_by_centri_acc = sqrtf(centripetal_acceleration * radius) / (arc_length * inv_distance);
|
||||
curr.feedrate = std::min(curr.feedrate, max_feedrate_by_centri_acc);
|
||||
|
||||
float min_feedrate_factor = 1.0f;
|
||||
for (unsigned char a = X; a <= E; ++a) {
|
||||
if (a == X || a == Y)
|
||||
//BBS: use resultant feedrate in x-y plane
|
||||
curr.axis_feedrate[a] = curr.feedrate * arc_length * inv_distance;
|
||||
else if (a == Z)
|
||||
curr.axis_feedrate[a] = curr.feedrate * delta_pos[a] * inv_distance;
|
||||
else
|
||||
curr.axis_feedrate[a] *= machine.extrude_factor_override_percentage;
|
||||
|
||||
curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]);
|
||||
if (curr.abs_axis_feedrate[a] != 0.0f) {
|
||||
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
|
||||
if (axis_max_feedrate != 0.0f) min_feedrate_factor = std::min<float>(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]);
|
||||
}
|
||||
}
|
||||
curr.feedrate *= min_feedrate_factor;
|
||||
block.feedrate_profile.cruise = curr.feedrate;
|
||||
if (min_feedrate_factor < 1.0f) {
|
||||
for (unsigned char a = X; a <= E; ++a) {
|
||||
curr.axis_feedrate[a] *= min_feedrate_factor;
|
||||
curr.abs_axis_feedrate[a] *= min_feedrate_factor;
|
||||
}
|
||||
}
|
||||
|
||||
//BBS: calculates block acceleration
|
||||
float acceleration = (type == EMoveType::Travel) ?
|
||||
get_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
|
||||
get_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i));
|
||||
float min_acc_factor = 1.0f;
|
||||
AxisCoords axis_acc;
|
||||
for (unsigned char a = X; a <= Z; ++a) {
|
||||
if (a == X || a == Y)
|
||||
//BBS: use resultant feedrate in x-y plane
|
||||
axis_acc[a] = acceleration * arc_length * inv_distance;
|
||||
else
|
||||
axis_acc[a] = acceleration * std::abs(delta_pos[a]) * inv_distance;
|
||||
|
||||
if (axis_acc[a] != 0.0f) {
|
||||
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
|
||||
if (axis_max_acceleration != 0.0f && axis_acc[a] > axis_max_acceleration) min_acc_factor = std::min<float>(min_acc_factor, axis_max_acceleration / axis_acc[a]);
|
||||
}
|
||||
}
|
||||
block.acceleration = acceleration * min_acc_factor;
|
||||
|
||||
//BBS: calculates block exit feedrate
|
||||
for (unsigned char a = X; a <= E; ++a) {
|
||||
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
|
||||
if (curr.abs_axis_feedrate[a] > axis_max_jerk)
|
||||
curr.safe_feedrate = std::min(curr.safe_feedrate, axis_max_jerk);
|
||||
}
|
||||
block.feedrate_profile.exit = curr.safe_feedrate;
|
||||
|
||||
//BBS: calculates block entry feedrate
|
||||
static const float PREVIOUS_FEEDRATE_THRESHOLD = 0.0001f;
|
||||
float vmax_junction = curr.safe_feedrate;
|
||||
if (!blocks.empty() && prev.feedrate > PREVIOUS_FEEDRATE_THRESHOLD) {
|
||||
bool prev_speed_larger = prev.feedrate > block.feedrate_profile.cruise;
|
||||
float smaller_speed_factor = prev_speed_larger ? (block.feedrate_profile.cruise / prev.feedrate) : (prev.feedrate / block.feedrate_profile.cruise);
|
||||
//BBS: Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
|
||||
vmax_junction = prev_speed_larger ? block.feedrate_profile.cruise : prev.feedrate;
|
||||
|
||||
float v_factor = 1.0f;
|
||||
bool limited = false;
|
||||
|
||||
for (unsigned char a = X; a <= E; ++a) {
|
||||
//BBS: Limit an axis. We have to differentiate coasting from the reversal of an axis movement, or a full stop.
|
||||
if (a == X) {
|
||||
Vec3f exit_v = prev.feedrate * (prev.exit_direction);
|
||||
if (prev_speed_larger)
|
||||
exit_v *= smaller_speed_factor;
|
||||
Vec3f entry_v = block.feedrate_profile.cruise * (curr.enter_direction);
|
||||
Vec3f jerk_v = entry_v - exit_v;
|
||||
jerk_v = Vec3f(abs(jerk_v.x()), abs(jerk_v.y()), abs(jerk_v.z()));
|
||||
Vec3f max_xyz_jerk_v = get_xyz_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i));
|
||||
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
{
|
||||
if (jerk_v[i] > max_xyz_jerk_v[i]) {
|
||||
v_factor *= max_xyz_jerk_v[i] / jerk_v[i];
|
||||
jerk_v *= v_factor;
|
||||
limited = true;
|
||||
}
|
||||
}
|
||||
Vec2f rvec(-rel_center.x(), -rel_center.y());
|
||||
AxisCoords arc_target = { 0.0f, 0.0f, m_start_position[Z], m_start_position[E] };
|
||||
for (size_t i = 1; i < segments; ++i) {
|
||||
if (--arc_recalc_count) {
|
||||
// Apply vector rotation matrix to previous rvec.a / 1
|
||||
const float r_new_Y = rvec.x() * sin_T + rvec.y() * cos_T;
|
||||
rvec.x() = rvec.x() * cos_T - rvec.y() * sin_T;
|
||||
rvec.y() = r_new_Y;
|
||||
}
|
||||
else if (a == Y || a == Z) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
float v_exit = prev.axis_feedrate[a];
|
||||
float v_entry = curr.axis_feedrate[a];
|
||||
|
||||
if (prev_speed_larger)
|
||||
v_exit *= smaller_speed_factor;
|
||||
|
||||
if (limited) {
|
||||
v_exit *= v_factor;
|
||||
v_entry *= v_factor;
|
||||
}
|
||||
|
||||
//BBS: Calculate the jerk depending on whether the axis is coasting in the same direction or reversing a direction.
|
||||
float jerk =
|
||||
(v_exit > v_entry) ?
|
||||
(((v_entry > 0.0f) || (v_exit < 0.0f)) ?
|
||||
//BBS: coasting
|
||||
(v_exit - v_entry) :
|
||||
//BBS: axis reversal
|
||||
std::max(v_exit, -v_entry)) :
|
||||
(((v_entry < 0.0f) || (v_exit > 0.0f)) ?
|
||||
//BBS: coasting
|
||||
(v_entry - v_exit) :
|
||||
//BBS: axis reversal
|
||||
std::max(-v_exit, v_entry));
|
||||
|
||||
|
||||
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
|
||||
if (jerk > axis_max_jerk) {
|
||||
v_factor *= axis_max_jerk / jerk;
|
||||
limited = true;
|
||||
}
|
||||
arc_recalc_count = N_ARC_CORRECTION;
|
||||
// Arc correction to radius vector. Computed only every N_ARC_CORRECTION increments.
|
||||
// Compute exact location by applying transformation matrix from initial radius vector(=-offset).
|
||||
// To reduce stuttering, the sin and cos could be computed at different times.
|
||||
// For now, compute both at the same time.
|
||||
const float Ti = i * theta_per_segment;
|
||||
const float cos_Ti = cos(Ti);
|
||||
const float sin_Ti = sin(Ti);
|
||||
rvec.x() = -rel_center.x() * cos_Ti + rel_center.y() * sin_Ti;
|
||||
rvec.y() = -rel_center.x() * sin_Ti - rel_center.y() * cos_Ti;
|
||||
}
|
||||
}
|
||||
|
||||
if (limited)
|
||||
vmax_junction *= v_factor;
|
||||
// Update arc_target location
|
||||
arc_target[X] = arc.center.x() + rvec.x();
|
||||
arc_target[Y] = arc.center.y() + rvec.y();
|
||||
arc_target[Z] += z_per_segment;
|
||||
arc_target[E] += extruder_per_segment;
|
||||
|
||||
//BBS: Now the transition velocity is known, which maximizes the shared exit / entry velocity while
|
||||
// respecting the jerk factors, it may be possible, that applying separate safe exit / entry velocities will achieve faster prints.
|
||||
float vmax_junction_threshold = vmax_junction * 0.99f;
|
||||
|
||||
//BBS: Not coasting. The machine will stop and start the movements anyway, better to start the segment from start.
|
||||
if ((prev.safe_feedrate > vmax_junction_threshold) && (curr.safe_feedrate > vmax_junction_threshold))
|
||||
vmax_junction = curr.safe_feedrate;
|
||||
}
|
||||
|
||||
float v_allowable = max_allowable_speed(-acceleration, curr.safe_feedrate, block.distance);
|
||||
block.feedrate_profile.entry = std::min(vmax_junction, v_allowable);
|
||||
|
||||
block.max_entry_speed = vmax_junction;
|
||||
block.flags.nominal_length = (block.feedrate_profile.cruise <= v_allowable);
|
||||
block.flags.recalculate = true;
|
||||
block.safe_feedrate = curr.safe_feedrate;
|
||||
|
||||
//BBS: calculates block trapezoid
|
||||
block.calculate_trapezoid();
|
||||
|
||||
//BBS: updates previous
|
||||
prev = curr;
|
||||
|
||||
blocks.push_back(block);
|
||||
|
||||
if (blocks.size() > TimeProcessor::Planner::refresh_threshold)
|
||||
machine.calculate_time(m_result, PrintEstimatedStatistics::ETimeMode::Normal, TimeProcessor::Planner::queue_size);
|
||||
}
|
||||
|
||||
//BBS: seam detector
|
||||
Vec3f plate_offset = {(float) m_x_offset, (float) m_y_offset, 0.0f};
|
||||
|
||||
if (m_seams_detector.is_active()) {
|
||||
//BBS: check for seam starting vertex
|
||||
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
|
||||
const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
|
||||
if (!m_seams_detector.has_first_vertex()) {
|
||||
m_seams_detector.set_first_vertex(new_pos);
|
||||
} else if (m_detect_layer_based_on_tag) {
|
||||
// We may have sloped loop, drop any previous start pos if we have z increment
|
||||
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex();
|
||||
if (new_pos.z() > first_vertex->z()) {
|
||||
m_seams_detector.set_first_vertex(new_pos);
|
||||
}
|
||||
m_start_position = m_end_position; // this is required because we are skipping the call to process_gcode_line()
|
||||
internal_only_g1_line(adjust_target(arc_target, prev_target), z_per_segment != 0.0, (i == 1) ? feedrate : std::nullopt,
|
||||
extrusion, segments - i);
|
||||
prev_target = arc_target;
|
||||
}
|
||||
}
|
||||
//BBS: check for seam ending vertex and store the resulting move
|
||||
else if ((type != EMoveType::Extrude || (m_extrusion_role != erExternalPerimeter && m_extrusion_role != erOverhangPerimeter)) && m_seams_detector.has_first_vertex()) {
|
||||
auto set_end_position = [this](const Vec3f& pos) {
|
||||
m_end_position[X] = pos.x(); m_end_position[Y] = pos.y(); m_end_position[Z] = pos.z();
|
||||
};
|
||||
const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]);
|
||||
const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
|
||||
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex();
|
||||
//BBS: the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later
|
||||
|
||||
if ((new_pos - *first_vertex).squaredNorm() < 0.0625f) {
|
||||
set_end_position(0.5f * (new_pos + *first_vertex));
|
||||
store_move_vertex(EMoveType::Seam);
|
||||
set_end_position(curr_pos);
|
||||
// Ensure last segment arrives at target location.
|
||||
m_start_position = m_end_position; // this is required because we are skipping the call to process_gcode_line()
|
||||
internal_only_g1_line(adjust_target(end_position, prev_target), arc.delta_z() != 0.0, (segments == 1) ? feedrate : std::nullopt, extrusion);
|
||||
}
|
||||
else {
|
||||
// calculate arc segments
|
||||
// reference:
|
||||
// Prusa-Firmware\Firmware\motion_control.cpp - mc_arc()
|
||||
// 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;
|
||||
const double z_per_segment = arc.delta_z() * inv_segment;
|
||||
const double extruder_per_segment = (extrusion.has_value()) ? *extrusion * inv_segment : 0.0;
|
||||
const double sq_theta_per_segment = sqr(theta_per_segment);
|
||||
const double cos_T = 1.0 - 0.5 * sq_theta_per_segment;
|
||||
const double sin_T = theta_per_segment - sq_theta_per_segment * theta_per_segment / 6.0f;
|
||||
|
||||
AxisCoords prev_target = m_start_position;
|
||||
AxisCoords arc_target;
|
||||
|
||||
// Initialize the linear axis
|
||||
arc_target[Z] = m_start_position[Z];
|
||||
|
||||
// Initialize the extruder axis
|
||||
arc_target[E] = m_start_position[E];
|
||||
|
||||
static const size_t N_ARC_CORRECTION = 25;
|
||||
Vec3d curr_rel_arc_start = arc.relative_start();
|
||||
size_t count = N_ARC_CORRECTION;
|
||||
|
||||
for (size_t i = 1; i < segments; ++i) {
|
||||
if (count-- == 0) {
|
||||
const double cos_Ti = ::cos(i * theta_per_segment);
|
||||
const double sin_Ti = ::sin(i * theta_per_segment);
|
||||
curr_rel_arc_start.x() = -double(rel_center.x()) * cos_Ti + double(rel_center.y()) * sin_Ti;
|
||||
curr_rel_arc_start.y() = -double(rel_center.x()) * sin_Ti - double(rel_center.y()) * cos_Ti;
|
||||
count = N_ARC_CORRECTION;
|
||||
}
|
||||
else {
|
||||
const float r_axisi = curr_rel_arc_start.x() * sin_T + curr_rel_arc_start.y() * cos_T;
|
||||
curr_rel_arc_start.x() = curr_rel_arc_start.x() * cos_T - curr_rel_arc_start.y() * sin_T;
|
||||
curr_rel_arc_start.y() = r_axisi;
|
||||
}
|
||||
|
||||
m_seams_detector.activate(false);
|
||||
// Update arc_target location
|
||||
arc_target[X] = arc.center.x() + curr_rel_arc_start.x();
|
||||
arc_target[Y] = arc.center.y() + curr_rel_arc_start.y();
|
||||
arc_target[Z] += z_per_segment;
|
||||
arc_target[E] += extruder_per_segment;
|
||||
|
||||
m_start_position = m_end_position; // this is required because we are skipping the call to process_gcode_line()
|
||||
internal_only_g1_line(adjust_target(arc_target, prev_target), z_per_segment != 0.0, (i == 1) ? feedrate : std::nullopt,
|
||||
extrusion, segments - i);
|
||||
prev_target = arc_target;
|
||||
}
|
||||
}
|
||||
else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
|
||||
m_seams_detector.activate(true);
|
||||
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// // Orca: we now use spiral_vase_layers for proper layer detect when scarf joint is enabled,
|
||||
// // and this is needed if the layer has only arc moves
|
||||
// if (m_detect_layer_based_on_tag && !m_result.spiral_vase_layers.empty()) {
|
||||
// if (delta_pos[Z] >= 0.0 && type == EMoveType::Extrude) {
|
||||
// const float current_z = static_cast<float>(m_end_position[Z]);
|
||||
// // replace layer height placeholder with correct value
|
||||
// if (m_result.spiral_vase_layers.back().first == FLT_MAX) {
|
||||
// m_result.spiral_vase_layers.back().first = current_z;
|
||||
// } else {
|
||||
// m_result.spiral_vase_layers.back().first = std::max(m_result.spiral_vase_layers.back().first, current_z);
|
||||
// }
|
||||
// }
|
||||
// if (!m_result.moves.empty())
|
||||
// m_result.spiral_vase_layers.back().second.second = m_result.moves.size() - 1 - m_seams_count;
|
||||
// }
|
||||
|
||||
//BBS: store move
|
||||
store_move_vertex(type, m_move_path_type);
|
||||
// Ensure last segment arrives at target location.
|
||||
m_start_position = m_end_position; // this is required because we are skipping the call to process_gcode_line()
|
||||
internal_only_g1_line(adjust_target(end_position, prev_target), arc.delta_z() != 0.0, (segments == 1) ? feedrate : std::nullopt, extrusion);
|
||||
}
|
||||
}
|
||||
|
||||
//BBS
|
||||
|
|
@ -4703,23 +4580,12 @@ void GCodeProcessor::run_post_process()
|
|||
"Is " + out_path + " locked?" + '\n');
|
||||
}
|
||||
|
||||
void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
|
||||
void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type, bool internal_only)
|
||||
{
|
||||
m_last_line_id = (type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ?
|
||||
m_line_id + 1 :
|
||||
((type == EMoveType::Seam) ? m_last_line_id : m_line_id);
|
||||
|
||||
//BBS: apply plate's and extruder's offset to arc interpolation points
|
||||
if (path_type == EMovePathType::Arc_move_cw ||
|
||||
path_type == EMovePathType::Arc_move_ccw) {
|
||||
for (size_t i = 0; i < m_interpolation_points.size(); i++)
|
||||
m_interpolation_points[i] =
|
||||
Vec3f(m_interpolation_points[i].x() + m_x_offset,
|
||||
m_interpolation_points[i].y() + m_y_offset,
|
||||
m_processing_start_custom_gcode ? m_first_layer_height : m_interpolation_points[i].z()) +
|
||||
m_extruder_offsets[m_extruder_id];
|
||||
}
|
||||
|
||||
m_result.moves.push_back({
|
||||
m_last_line_id,
|
||||
type,
|
||||
|
|
@ -4740,10 +4606,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
|
|||
{ 0.0f, 0.0f }, // time
|
||||
std::max<unsigned int>(1, m_layer_id) - 1,
|
||||
static_cast<float>(m_layer_id), //layer_duration: set later
|
||||
//BBS: add arc move related data
|
||||
path_type,
|
||||
Vec3f(m_arc_center(0, 0) + m_x_offset, m_arc_center(1, 0) + m_y_offset, m_arc_center(2, 0)) + m_extruder_offsets[m_extruder_id],
|
||||
m_interpolation_points,
|
||||
internal_only
|
||||
});
|
||||
|
||||
if (type == EMoveType::Seam) {
|
||||
|
|
@ -4961,7 +4824,7 @@ void GCodeProcessor::calculate_time(GCodeProcessorResult& result, size_t keep_la
|
|||
new_move.mm3_per_mm = *it->mm3_per_mm;
|
||||
new_move.fan_speed = *it->fan_speed;
|
||||
new_move.temperature = *it->temperature;
|
||||
// new_move.internal_only = true; // TODO
|
||||
new_move.internal_only = true;
|
||||
new_moves.push_back(new_move);
|
||||
}
|
||||
else {
|
||||
|
|
@ -5018,6 +4881,23 @@ void GCodeProcessor::update_estimated_times_stats()
|
|||
m_result.print_statistics.total_volumes_per_extruder = m_used_filaments.total_volumes_per_extruder;
|
||||
}
|
||||
|
||||
double GCodeProcessor::extract_absolute_position_on_axis(Axis axis, const GCodeReader::GCodeLine& line, double area_filament_cross_section)
|
||||
{
|
||||
if (line.has(Slic3r::Axis(axis))) {
|
||||
bool is_relative = (m_global_positioning_type == EPositioningType::Relative);
|
||||
if (axis == E)
|
||||
is_relative |= (m_e_local_positioning_type == EPositioningType::Relative);
|
||||
|
||||
const double lengthsScaleFactor = (m_units == EUnits::Inches) ? double(INCHES_TO_MM) : 1.0;
|
||||
double ret = line.value(Slic3r::Axis(axis)) * lengthsScaleFactor;
|
||||
// if (axis == E && m_use_volumetric_e)
|
||||
// ret /= area_filament_cross_section;
|
||||
return is_relative ? m_start_position[axis] + ret : m_origin[axis] + ret;
|
||||
}
|
||||
else
|
||||
return m_start_position[axis];
|
||||
}
|
||||
|
||||
//BBS: ugly code...
|
||||
void GCodeProcessor::update_slice_warnings()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -160,22 +160,10 @@ class Print;
|
|||
std::array<float, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> time{ 0.0f, 0.0f }; // s
|
||||
unsigned int layer_id{ 0 };
|
||||
float layer_duration{ 0.0f }; // s (layer id before finalize)
|
||||
|
||||
|
||||
//BBS: arc move related data
|
||||
EMovePathType move_path_type{ EMovePathType::Noop_move };
|
||||
Vec3f arc_center_position{ Vec3f::Zero() }; // mm
|
||||
std::vector<Vec3f> interpolation_points; // interpolation points of arc for drawing
|
||||
bool internal_only{ false };
|
||||
|
||||
float volumetric_rate() const { return feedrate * mm3_per_mm; }
|
||||
float actual_volumetric_rate() const { return actual_feedrate * mm3_per_mm; }
|
||||
//BBS: new function to support arc move
|
||||
bool is_arc_move_with_interpolation_points() const {
|
||||
return (move_path_type == EMovePathType::Arc_move_ccw || move_path_type == EMovePathType::Arc_move_cw) && interpolation_points.size();
|
||||
}
|
||||
bool is_arc_move() const {
|
||||
return move_path_type == EMovePathType::Arc_move_ccw || move_path_type == EMovePathType::Arc_move_cw;
|
||||
}
|
||||
};
|
||||
|
||||
struct SliceWarning {
|
||||
|
|
@ -698,10 +686,6 @@ class Print;
|
|||
//BBS: x, y offset for gcode generated
|
||||
double m_x_offset{ 0 };
|
||||
double m_y_offset{ 0 };
|
||||
//BBS: arc move related data
|
||||
EMovePathType m_move_path_type{ EMovePathType::Noop_move };
|
||||
Vec3f m_arc_center{ Vec3f::Zero() }; // mm
|
||||
std::vector<Vec3f> m_interpolation_points;
|
||||
|
||||
unsigned int m_line_id;
|
||||
unsigned int m_last_line_id;
|
||||
|
|
@ -830,7 +814,16 @@ class Print;
|
|||
// Move
|
||||
void process_G0(const GCodeReader::GCodeLine& line);
|
||||
void process_G1(const GCodeReader::GCodeLine& line, const std::optional<unsigned int>& remaining_internal_g1_lines = std::nullopt);
|
||||
void process_G2_G3(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 },
|
||||
const std::optional<double>& feedrate = std::nullopt, G1DiscretizationOrigin origin = G1DiscretizationOrigin::G1,
|
||||
const std::optional<unsigned int>& remaining_internal_g1_lines = std::nullopt);
|
||||
|
||||
// Arc Move
|
||||
void process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise);
|
||||
|
||||
// BBS: handle delay command
|
||||
void process_G4(const GCodeReader::GCodeLine& line);
|
||||
|
|
@ -950,7 +943,7 @@ class Print;
|
|||
void run_post_process();
|
||||
|
||||
//BBS: different path_type is only used for arc move
|
||||
void store_move_vertex(EMoveType type, EMovePathType path_type = EMovePathType::Noop_move);
|
||||
void store_move_vertex(EMoveType type, EMovePathType path_type = EMovePathType::Noop_move, bool internal_only = false);
|
||||
|
||||
void set_extrusion_role(ExtrusionRole role);
|
||||
|
||||
|
|
@ -978,6 +971,9 @@ class Print;
|
|||
void simulate_st_synchronize(float additional_time = 0.0f);
|
||||
|
||||
void update_estimated_times_stats();
|
||||
|
||||
double extract_absolute_position_on_axis(Axis axis, const GCodeReader::GCodeLine& line, double area_filament_cross_section);
|
||||
|
||||
//BBS:
|
||||
void update_slice_warnings();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -222,6 +222,28 @@ bool GCodeReader::parse_file_raw(const std::string &filename, raw_line_callback_
|
|||
[](size_t){});
|
||||
}
|
||||
|
||||
const char* GCodeReader::axis_pos(const char *raw_str, char axis)
|
||||
{
|
||||
const char *c = raw_str;
|
||||
// Skip the whitespaces.
|
||||
c = skip_whitespaces(c);
|
||||
// Skip the command.
|
||||
c = skip_word(c);
|
||||
// Up to the end of line or comment.
|
||||
while (! is_end_of_gcode_line(*c)) {
|
||||
// Skip whitespaces.
|
||||
c = skip_whitespaces(c);
|
||||
if (is_end_of_gcode_line(*c))
|
||||
break;
|
||||
// Check the name of the axis.
|
||||
if (*c == axis)
|
||||
return c;
|
||||
// Skip the rest of the word.
|
||||
c = skip_word(c);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool GCodeReader::GCodeLine::has(char axis) const
|
||||
{
|
||||
const char *c = m_raw.c_str();
|
||||
|
|
@ -244,6 +266,29 @@ bool GCodeReader::GCodeLine::has(char axis) const
|
|||
return false;
|
||||
}
|
||||
|
||||
std::string_view GCodeReader::GCodeLine::axis_pos(char axis) const
|
||||
{
|
||||
const std::string &s = this->raw();
|
||||
const char *c = GCodeReader::axis_pos(this->raw().c_str(), axis);
|
||||
return c ? std::string_view{ c, s.size() - (c - s.data()) } : std::string_view();
|
||||
}
|
||||
|
||||
bool GCodeReader::GCodeLine::has_value(std::string_view axis_pos, float &value)
|
||||
{
|
||||
if (const char *c = axis_pos.data(); c) {
|
||||
// Try to parse the numeric value.
|
||||
double v = 0.;
|
||||
const char *end = axis_pos.data() + axis_pos.size();
|
||||
auto [pend, ec] = fast_float::from_chars(++ c, end, v);
|
||||
if (pend != c && is_end_of_word(*pend)) {
|
||||
// The axis value has been parsed correctly.
|
||||
value = float(v);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GCodeReader::GCodeLine::has_value(char axis, float &value) const
|
||||
{
|
||||
assert(is_decimal_separator_point());
|
||||
|
|
|
|||
|
|
@ -26,11 +26,15 @@ public:
|
|||
const std::string_view comment() const
|
||||
{ size_t pos = m_raw.find(';'); return (pos == std::string::npos) ? std::string_view() : std::string_view(m_raw).substr(pos + 1); }
|
||||
|
||||
// Return position in this->raw() string starting with the "axis" character.
|
||||
std::string_view axis_pos(char axis) const;
|
||||
void clear() { m_raw.clear(); }
|
||||
bool has(Axis axis) const { return (m_mask & (1 << int(axis))) != 0; }
|
||||
float value(Axis axis) const { return m_axis[axis]; }
|
||||
bool has(char axis) const;
|
||||
bool has_value(char axis, float &value) const;
|
||||
// Parse value of an axis from raw string starting at axis_pos.
|
||||
static bool has_value(std::string_view axis_pos, float &value);
|
||||
float new_X(const GCodeReader &reader) const { return this->has(X) ? this->x() : reader.x(); }
|
||||
float new_Y(const GCodeReader &reader) const { return this->has(Y) ? this->y() : reader.y(); }
|
||||
float new_Z(const GCodeReader &reader) const { return this->has(Z) ? this->z() : reader.z(); }
|
||||
|
|
@ -185,6 +189,7 @@ private:
|
|||
; // silence -Wempty-body
|
||||
return c;
|
||||
}
|
||||
static const char* axis_pos(const char *raw_str, char axis);
|
||||
|
||||
GCodeConfig m_config;
|
||||
float m_position[NUM_AXES];
|
||||
|
|
|
|||
32
src/libslic3r/Geometry/ArcWelder.cpp
Normal file
32
src/libslic3r/Geometry/ArcWelder.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// The following code for merging circles into arches originates from https://github.com/FormerLurker/ArcWelderLib
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Arc Welder: Anti-Stutter Library
|
||||
//
|
||||
// Compresses many G0/G1 commands into G2/G3(arc) commands where possible, ensuring the tool paths stay within the specified resolution.
|
||||
// This reduces file size and the number of gcodes per second.
|
||||
//
|
||||
// Uses the 'Gcode Processor Library' for gcode parsing, position processing, logging, and other various functionality.
|
||||
//
|
||||
// Copyright(C) 2021 - Brad Hochgesang
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// This program is free software : you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You can contact the author at the following email address:
|
||||
// FormerLurker@pm.me
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "ArcWelder.hpp"
|
||||
|
||||
namespace Slic3r { namespace Geometry { namespace ArcWelder {
|
||||
|
||||
} } } // namespace Slic3r::Geometry::ArcWelder
|
||||
71
src/libslic3r/Geometry/ArcWelder.hpp
Normal file
71
src/libslic3r/Geometry/ArcWelder.hpp
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#ifndef slic3r_Geometry_ArcWelder_hpp_
|
||||
#define slic3r_Geometry_ArcWelder_hpp_
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <Eigen/Geometry>
|
||||
#include <type_traits>
|
||||
#include <cassert>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
namespace Slic3r { namespace Geometry { namespace ArcWelder {
|
||||
|
||||
// Calculate center point (center of a circle) of an arc given two points and a radius.
|
||||
// positive radius: take shorter arc
|
||||
// negative radius: take longer arc
|
||||
// radius must NOT be zero!
|
||||
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 Float radius,
|
||||
const bool is_ccw)
|
||||
{
|
||||
static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "arc_center(): first parameter is not a 2D vector");
|
||||
static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "arc_center(): second parameter is not a 2D vector");
|
||||
static_assert(std::is_same<typename Derived::Scalar, typename Derived2::Scalar>::value, "arc_center(): Both vectors must be of the same type.");
|
||||
static_assert(std::is_same<typename Derived::Scalar, Float>::value, "arc_center(): Radius must be of the same type as the vectors.");
|
||||
assert(radius != 0);
|
||||
using Vector = Eigen::Matrix<Float, 2, 1, Eigen::DontAlign>;
|
||||
auto v = end_pos - start_pos;
|
||||
Float q2 = v.squaredNorm();
|
||||
assert(q2 > 0);
|
||||
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);
|
||||
Vector vp{ -v.y() * t, v.x() * t };
|
||||
return (radius > Float(0)) == is_ccw ? (mid + vp).eval() : (mid - vp).eval();
|
||||
}
|
||||
|
||||
|
||||
// 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))));
|
||||
}
|
||||
|
||||
} } } // namespace Slic3r::Geometry::ArcWelder
|
||||
|
||||
#endif // slic3r_Geometry_ArcWelder_hpp_
|
||||
Loading…
Add table
Add a link
Reference in a new issue