mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 05:56:02 +08:00
Add helical layer change.
Previously the layer change was straight up which caused stringing. To solve this, a helical travel is implemented when changing layers.
This commit is contained in:
parent
49455cf427
commit
a4a7ac4a4e
@ -2172,6 +2172,7 @@ LayerResult GCodeGenerator::process_layer(
|
|||||||
+ float_to_string_decimal_point(height) + "\n";
|
+ float_to_string_decimal_point(height) + "\n";
|
||||||
|
|
||||||
// update caches
|
// update caches
|
||||||
|
const coordf_t previous_layer_z{m_last_layer_z};
|
||||||
m_last_layer_z = static_cast<float>(print_z);
|
m_last_layer_z = static_cast<float>(print_z);
|
||||||
m_max_layer_z = std::max(m_max_layer_z, m_last_layer_z);
|
m_max_layer_z = std::max(m_max_layer_z, m_last_layer_z);
|
||||||
m_last_height = height;
|
m_last_height = height;
|
||||||
@ -2186,7 +2187,7 @@ LayerResult GCodeGenerator::process_layer(
|
|||||||
print.config().before_layer_gcode.value, m_writer.extruder()->id(), &config)
|
print.config().before_layer_gcode.value, m_writer.extruder()->id(), &config)
|
||||||
+ "\n";
|
+ "\n";
|
||||||
}
|
}
|
||||||
gcode += this->change_layer(print_z); // this will increase m_layer_index
|
gcode += this->change_layer(previous_layer_z, print_z); // this will increase m_layer_index
|
||||||
m_layer = &layer;
|
m_layer = &layer;
|
||||||
m_object_layer_over_raft = false;
|
m_object_layer_over_raft = false;
|
||||||
if (! print.config().layer_gcode.value.empty()) {
|
if (! print.config().layer_gcode.value.empty()) {
|
||||||
@ -2608,22 +2609,143 @@ std::string GCodeGenerator::preamble()
|
|||||||
return gcode;
|
return gcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace GCode::Impl {
|
||||||
|
Polygon generate_regular_polygon(
|
||||||
|
const Point& centroid,
|
||||||
|
const Point& start_point,
|
||||||
|
const unsigned points_count
|
||||||
|
) {
|
||||||
|
Points points;
|
||||||
|
points.reserve(points_count);
|
||||||
|
const double part_angle{2*M_PI / points_count};
|
||||||
|
for (unsigned i = 0; i < points_count; ++i) {
|
||||||
|
const double current_angle{i * part_angle};
|
||||||
|
points.emplace_back(scaled(std::cos(current_angle)), scaled(std::sin(current_angle)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Polygon regular_polygon{points};
|
||||||
|
const Vec2d current_vector{unscaled(regular_polygon.points.front())};
|
||||||
|
const Vec2d expected_vector{unscaled(start_point) - unscaled(centroid)};
|
||||||
|
|
||||||
|
const double current_scale = current_vector.norm();
|
||||||
|
const double expected_scale = expected_vector.norm();
|
||||||
|
regular_polygon.scale(expected_scale / current_scale);
|
||||||
|
|
||||||
|
regular_polygon.rotate(angle(current_vector, expected_vector));
|
||||||
|
|
||||||
|
regular_polygon.translate(centroid);
|
||||||
|
|
||||||
|
return regular_polygon;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bed::Bed(const std::vector<Vec2d>& shape, const double padding):
|
||||||
|
inner_offset(get_inner_offset(shape, padding)),
|
||||||
|
centroid(unscaled(inner_offset.centroid()))
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool Bed::contains_within_padding(const Vec2d& point) const {
|
||||||
|
return inner_offset.contains(scaled(point));
|
||||||
|
}
|
||||||
|
|
||||||
|
Polygon Bed::get_inner_offset(const std::vector<Vec2d>& shape, const double padding) {
|
||||||
|
Points shape_scaled;
|
||||||
|
shape_scaled.reserve(shape.size());
|
||||||
|
using std::begin, std::end, std::back_inserter, std::transform;
|
||||||
|
transform(begin(shape), end(shape), back_inserter(shape_scaled), [](const Vec2d& point){
|
||||||
|
return scaled(point);
|
||||||
|
});
|
||||||
|
return shrink({Polygon{shape_scaled}}, scaled(padding)).front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> GCodeGenerator::get_helical_layer_change_gcode(
|
||||||
|
const coordf_t previous_layer_z,
|
||||||
|
const coordf_t print_z,
|
||||||
|
const std::string& comment
|
||||||
|
) {
|
||||||
|
|
||||||
|
if (!this->last_pos_defined()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double circle_radius{2};
|
||||||
|
const unsigned n_gon_points_count{16};
|
||||||
|
|
||||||
|
const Point n_gon_start_point{this->last_pos()};
|
||||||
|
|
||||||
|
static GCode::Impl::Bed bed{
|
||||||
|
this->m_config.bed_shape.values,
|
||||||
|
circle_radius
|
||||||
|
};
|
||||||
|
if (!bed.contains_within_padding(this->point_to_gcode(n_gon_start_point))) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Point n_gon_centeroid{
|
||||||
|
n_gon_start_point
|
||||||
|
+ scaled(Vec2d{
|
||||||
|
(bed.centroid - unscaled(n_gon_start_point)).normalized()
|
||||||
|
* circle_radius
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const Polygon n_gon{GCode::Impl::generate_regular_polygon(
|
||||||
|
n_gon_centeroid,
|
||||||
|
n_gon_start_point,
|
||||||
|
n_gon_points_count
|
||||||
|
)};
|
||||||
|
|
||||||
|
const double n_gon_circumference = unscaled(n_gon.length());
|
||||||
|
|
||||||
|
const double z_change{print_z - previous_layer_z};
|
||||||
|
Points3 helix{GCode::Impl::generate_elevated_travel(
|
||||||
|
n_gon.points,
|
||||||
|
{},
|
||||||
|
previous_layer_z,
|
||||||
|
[&](const double distance){
|
||||||
|
return distance / n_gon_circumference * z_change;
|
||||||
|
}
|
||||||
|
)};
|
||||||
|
|
||||||
|
helix.emplace_back(to_3d(this->last_pos(), scaled(print_z)));
|
||||||
|
|
||||||
|
return this->generate_travel_gcode(helix, comment);
|
||||||
|
}
|
||||||
|
|
||||||
// called by GCodeGenerator::process_layer()
|
// called by GCodeGenerator::process_layer()
|
||||||
std::string GCodeGenerator::change_layer(coordf_t print_z)
|
std::string GCodeGenerator::change_layer(coordf_t previous_layer_z, coordf_t print_z)
|
||||||
{
|
{
|
||||||
std::string gcode;
|
std::string gcode;
|
||||||
if (m_layer_count > 0)
|
if (m_layer_count > 0)
|
||||||
// Increment a progress bar indicator.
|
// Increment a progress bar indicator.
|
||||||
gcode += m_writer.update_progress(++ m_layer_index, m_layer_count);
|
gcode += m_writer.update_progress(++ m_layer_index, m_layer_count);
|
||||||
coordf_t z = print_z + m_config.z_offset.value; // in unscaled coordinates
|
|
||||||
if (EXTRUDER_CONFIG(retract_layer_change))
|
if (EXTRUDER_CONFIG(retract_layer_change))
|
||||||
gcode += this->retract_and_wipe();
|
gcode += this->retract_and_wipe();
|
||||||
|
|
||||||
{
|
const std::string comment{"move to next layer (" + std::to_string(m_layer_index) + ")"};
|
||||||
std::ostringstream comment;
|
|
||||||
comment << "move to next layer (" << m_layer_index << ")";
|
bool helical_layer_change{
|
||||||
gcode += m_writer.travel_to_z(z, comment.str());
|
(!this->m_spiral_vase || !this->m_spiral_vase->is_enabled())
|
||||||
}
|
&& print_z > previous_layer_z
|
||||||
|
&& EXTRUDER_CONFIG(travel_lift_before_obstacle)
|
||||||
|
&& EXTRUDER_CONFIG(travel_slope) > 0 && EXTRUDER_CONFIG(travel_slope) < 90
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::optional<std::string> helix_gcode{
|
||||||
|
helical_layer_change ?
|
||||||
|
this->get_helical_layer_change_gcode(
|
||||||
|
m_config.z_offset.value + previous_layer_z,
|
||||||
|
m_config.z_offset.value + print_z,
|
||||||
|
comment
|
||||||
|
) :
|
||||||
|
std::nullopt
|
||||||
|
};
|
||||||
|
gcode += (
|
||||||
|
helix_gcode ?
|
||||||
|
*helix_gcode :
|
||||||
|
m_writer.travel_to_z(m_config.z_offset.value + print_z, comment)
|
||||||
|
);
|
||||||
|
|
||||||
// forget last wiping path as wiping after raising Z is pointless
|
// forget last wiping path as wiping after raising Z is pointless
|
||||||
m_wipe.reset_path();
|
m_wipe.reset_path();
|
||||||
|
@ -135,6 +135,39 @@ Points3 generate_elevated_travel(
|
|||||||
const std::function<double(double)>& elevation
|
const std::function<double(double)>& elevation
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a regular polygon - all angles are the same (e.g. typical hexagon).
|
||||||
|
*
|
||||||
|
* @param centroid Central point.
|
||||||
|
* @param start_point The polygon point are ordered. This is the first point.
|
||||||
|
* @param points_count Amount of nodes of the polygon (e.g. 6 for haxagon).
|
||||||
|
*
|
||||||
|
* Distance between centroid and start point sets the scale of the polygon.
|
||||||
|
*/
|
||||||
|
Polygon generate_regular_polygon(
|
||||||
|
const Point& centroid,
|
||||||
|
const Point& start_point,
|
||||||
|
const unsigned points_count
|
||||||
|
);
|
||||||
|
|
||||||
|
class Bed {
|
||||||
|
private:
|
||||||
|
Polygon inner_offset;
|
||||||
|
static Polygon get_inner_offset(const std::vector<Vec2d>& shape, const double padding);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Bed shape with inner padding.
|
||||||
|
*/
|
||||||
|
Bed(const std::vector<Vec2d>& shape, const double padding);
|
||||||
|
|
||||||
|
Vec2d centroid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the point is within the bed shape including inner padding.
|
||||||
|
*/
|
||||||
|
bool contains_within_padding(const Vec2d& point) const;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
class GCodeGenerator {
|
class GCodeGenerator {
|
||||||
|
|
||||||
@ -302,7 +335,12 @@ private:
|
|||||||
bool last_pos_defined() const { return m_last_pos_defined; }
|
bool last_pos_defined() const { return m_last_pos_defined; }
|
||||||
void set_extruders(const std::vector<unsigned int> &extruder_ids);
|
void set_extruders(const std::vector<unsigned int> &extruder_ids);
|
||||||
std::string preamble();
|
std::string preamble();
|
||||||
std::string change_layer(coordf_t print_z);
|
std::optional<std::string> get_helical_layer_change_gcode(
|
||||||
|
const coordf_t previous_layer_z,
|
||||||
|
const coordf_t print_z,
|
||||||
|
const std::string& comment
|
||||||
|
);
|
||||||
|
std::string change_layer(coordf_t previous_layer_z, coordf_t print_z);
|
||||||
std::string extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.);
|
std::string extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.);
|
||||||
std::string extrude_loop(const ExtrusionLoop &loop, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.);
|
std::string extrude_loop(const ExtrusionLoop &loop, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.);
|
||||||
std::string extrude_skirt(const ExtrusionLoop &loop_src, const ExtrusionFlow &extrusion_flow_override,
|
std::string extrude_skirt(const ExtrusionLoop &loop_src, const ExtrusionFlow &extrusion_flow_override,
|
||||||
|
@ -27,8 +27,12 @@ public:
|
|||||||
m_enabled = en;
|
m_enabled = en;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_enabled() const {
|
||||||
|
return m_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
std::string process_layer(const std::string &gcode);
|
std::string process_layer(const std::string &gcode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const PrintConfig &m_config;
|
const PrintConfig &m_config;
|
||||||
GCodeReader m_reader;
|
GCodeReader m_reader;
|
||||||
|
@ -139,3 +139,53 @@ TEST_CASE("Generate elevated travel", "[GCode]") {
|
|||||||
scaled(Vec3f{1, 0, 4.0})
|
scaled(Vec3f{1, 0, 4.0})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Generate regular polygon", "[GCode]") {
|
||||||
|
const unsigned points_count{32};
|
||||||
|
const Point centroid{scaled(Vec2d{5, -2})};
|
||||||
|
const Polygon result{generate_regular_polygon(centroid, scaled(Vec2d{0, 0}), points_count)};
|
||||||
|
const Point oposite_point{centroid * 2};
|
||||||
|
|
||||||
|
REQUIRE(result.size() == 32);
|
||||||
|
CHECK(result[16].x() == Approx(oposite_point.x()));
|
||||||
|
CHECK(result[16].y() == Approx(oposite_point.y()));
|
||||||
|
|
||||||
|
std::vector<double> angles;
|
||||||
|
angles.reserve(points_count);
|
||||||
|
for (unsigned index = 0; index < points_count; index++) {
|
||||||
|
const unsigned previous_index{index == 0 ? points_count - 1 : index - 1};
|
||||||
|
const unsigned next_index{index == points_count - 1 ? 0 : index + 1};
|
||||||
|
|
||||||
|
const Point previous_point = result.points[previous_index];
|
||||||
|
const Point current_point = result.points[index];
|
||||||
|
const Point next_point = result.points[next_index];
|
||||||
|
|
||||||
|
angles.emplace_back(angle(Vec2crd{previous_point - current_point}, Vec2crd{next_point - current_point}));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double> expected;
|
||||||
|
angles.reserve(points_count);
|
||||||
|
std::generate_n(std::back_inserter(expected), points_count, [&](){
|
||||||
|
return angles.front();
|
||||||
|
});
|
||||||
|
|
||||||
|
CHECK_THAT(angles, Catch::Matchers::Approx(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Square bed with padding", "[GCode]") {
|
||||||
|
const Bed bed{
|
||||||
|
{
|
||||||
|
Vec2d{0, 0},
|
||||||
|
Vec2d{100, 0},
|
||||||
|
Vec2d{100, 100},
|
||||||
|
Vec2d{0, 100}
|
||||||
|
},
|
||||||
|
10.0
|
||||||
|
};
|
||||||
|
|
||||||
|
CHECK(bed.centroid.x() == 50);
|
||||||
|
CHECK(bed.centroid.y() == 50);
|
||||||
|
CHECK(bed.contains_within_padding(Vec2d{10, 10}));
|
||||||
|
CHECK_FALSE(bed.contains_within_padding(Vec2d{9, 10}));
|
||||||
|
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user