Prusa 2.7.3

This commit is contained in:
sunsets
2024-03-30 10:22:25 +08:00
parent 764ce01063
commit 5ccb55ff98
56 changed files with 2106 additions and 1483 deletions

View File

@@ -1171,7 +1171,7 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCodeGenerator &gcodegen, cons
{
// If use_external, then perform the path planning in the world coordinate system (correcting for the gcodegen offset).
// Otherwise perform the path planning in the coordinate system of the active object.
bool use_external = m_use_external_mp || m_use_external_mp_once;
bool use_external = m_use_external_mp || use_external_mp_once;
Point scaled_origin = use_external ? Point::new_scale(gcodegen.origin()(0), gcodegen.origin()(1)) : Point(0, 0);
const Point start = *gcodegen.last_position + scaled_origin;
const Point end = point + scaled_origin;

View File

@@ -17,11 +17,10 @@ class AvoidCrossingPerimeters
public:
// Routing around the objects vs. inside a single object.
void use_external_mp(bool use = true) { m_use_external_mp = use; };
void use_external_mp_once() { m_use_external_mp_once = true; }
bool used_external_mp_once() { return m_use_external_mp_once; }
bool used_external_mp_once() { return use_external_mp_once; }
void disable_once() { m_disabled_once = true; }
bool disabled_once() const { return m_disabled_once; }
void reset_once_modifiers() { m_use_external_mp_once = false; m_disabled_once = false; }
void reset_once_modifiers() { use_external_mp_once = false; m_disabled_once = false; }
void init_layer(const Layer &layer);
@@ -50,10 +49,10 @@ public:
}
};
// just for the next travel move
bool use_external_mp_once { false };
private:
bool m_use_external_mp { false };
// just for the next travel move
bool m_use_external_mp_once { false };
// this flag disables avoid_crossing_perimeters just for the next travel move
// we enable it by default for the first travel move in print
bool m_disabled_once { true };

View File

@@ -3806,7 +3806,13 @@ void GCodeProcessor::post_process()
struct LineData
{
std::string line;
float time;
std::array<float, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> times{ 0.0f, 0.0f };
};
enum ETimeMode
{
Normal = static_cast<int>(PrintEstimatedStatistics::ETimeMode::Normal),
Stealth = static_cast<int>(PrintEstimatedStatistics::ETimeMode::Stealth)
};
#ifndef NDEBUG
@@ -3836,10 +3842,10 @@ void GCodeProcessor::post_process()
#endif // NDEBUG
EWriteType m_write_type{ EWriteType::BySize };
// Time machine containing g1 times cache
TimeMachine& m_machine;
// Time machines containing g1 times cache
const std::array<TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)>& m_machines;
// Current time
float m_time{ 0.0f };
std::array<float, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> m_times{ 0.0f, 0.0f };
// Current size in bytes
size_t m_size{ 0 };
@@ -3855,11 +3861,12 @@ void GCodeProcessor::post_process()
bgcode::binarize::Binarizer& m_binarizer;
public:
ExportLines(bgcode::binarize::Binarizer& binarizer, EWriteType type, TimeMachine& machine)
ExportLines(bgcode::binarize::Binarizer& binarizer, EWriteType type,
const std::array<TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)>& machines)
#ifndef NDEBUG
: m_statistics(*this), m_binarizer(binarizer), m_write_type(type), m_machine(machine) {}
: m_statistics(*this), m_binarizer(binarizer), m_write_type(type), m_machines(machines) {}
#else
: m_binarizer(binarizer), m_write_type(type), m_machine(machine) {}
: m_binarizer(binarizer), m_write_type(type), m_machines(machines) {}
#endif // NDEBUG
// return: number of internal G1 lines (from G2/G3 splitting) processed
@@ -3876,9 +3883,9 @@ void GCodeProcessor::post_process()
else
return ret;
auto init_it = m_machine.g1_times_cache.begin() + m_times_cache_id;
auto init_it = m_machines[Normal].g1_times_cache.begin() + m_times_cache_id;
auto it = init_it;
while (it != m_machine.g1_times_cache.end() && it->id < g1_lines_counter) {
while (it != m_machines[Normal].g1_times_cache.end() && it->id < g1_lines_counter) {
++it;
++m_times_cache_id;
}
@@ -3888,7 +3895,7 @@ void GCodeProcessor::post_process()
// search for internal G1 lines
if (GCodeReader::GCodeLine::cmd_is(line, "G2") || GCodeReader::GCodeLine::cmd_is(line, "G3")) {
while (it != m_machine.g1_times_cache.end() && it->remaining_internal_g1_lines > 0) {
while (it != m_machines[Normal].g1_times_cache.end() && it->remaining_internal_g1_lines > 0) {
++it;
++m_times_cache_id;
++g1_lines_counter;
@@ -3896,14 +3903,17 @@ void GCodeProcessor::post_process()
}
}
if (it != m_machine.g1_times_cache.end() && it->id == g1_lines_counter)
m_time = it->elapsed_time;
if (it != m_machines[Normal].g1_times_cache.end() && it->id == g1_lines_counter) {
m_times[Normal] = it->elapsed_time;
if (!m_machines[Stealth].g1_times_cache.empty())
m_times[Stealth] = (m_machines[Stealth].g1_times_cache.begin() + std::distance(m_machines[Normal].g1_times_cache.begin(), it))->elapsed_time;
}
return ret;
}
// add the given gcode line to the cache
void append_line(const std::string& line) {
m_lines.push_back({ line, m_time });
m_lines.push_back({ line, m_times });
#ifndef NDEBUG
m_statistics.add_line(line.length());
#endif // NDEBUG
@@ -3914,7 +3924,8 @@ void GCodeProcessor::post_process()
}
// Insert the gcode lines required by the command cmd by backtracing into the cache
void insert_lines(const Backtrace& backtrace, const std::string& cmd, std::function<std::string(unsigned int, float, float)> line_inserter,
void insert_lines(const Backtrace& backtrace, const std::string& cmd,
std::function<std::string(unsigned int, const std::vector<float>&)> line_inserter,
std::function<std::string(const std::string&)> line_replacer) {
assert(!m_lines.empty());
const float time_step = backtrace.time_step();
@@ -3922,13 +3933,13 @@ void GCodeProcessor::post_process()
float last_time_insertion = 0.0f; // used to avoid inserting two lines at the same time
for (unsigned int i = 0; i < backtrace.steps; ++i) {
const float backtrace_time_i = (i + 1) * time_step;
const float time_threshold_i = m_time - backtrace_time_i;
const float time_threshold_i = m_times[Normal] - backtrace_time_i;
auto rev_it = m_lines.rbegin() + rev_it_dist;
auto start_rev_it = rev_it;
std::string curr_cmd = GCodeReader::GCodeLine::extract_cmd(rev_it->line);
// backtrace into the cache to find the place where to insert the line
while (rev_it != m_lines.rend() && rev_it->time > time_threshold_i && curr_cmd != cmd && curr_cmd != "G28" && curr_cmd != "G29") {
while (rev_it != m_lines.rend() && rev_it->times[Normal] > time_threshold_i && curr_cmd != cmd && curr_cmd != "G28" && curr_cmd != "G29") {
rev_it->line = line_replacer(rev_it->line);
++rev_it;
if (rev_it != m_lines.rend())
@@ -3940,11 +3951,15 @@ void GCodeProcessor::post_process()
break;
// insert the line for the current step
if (rev_it != m_lines.rend() && rev_it != start_rev_it && rev_it->time != last_time_insertion) {
last_time_insertion = rev_it->time;
const std::string out_line = line_inserter(i + 1, last_time_insertion, m_time - last_time_insertion);
if (rev_it != m_lines.rend() && rev_it != start_rev_it && rev_it->times[Normal] != last_time_insertion) {
last_time_insertion = rev_it->times[Normal];
std::vector<float> time_diffs;
time_diffs.push_back(m_times[Normal] - last_time_insertion);
if (!m_machines[Stealth].g1_times_cache.empty())
time_diffs.push_back(m_times[Stealth] - rev_it->times[Stealth]);
const std::string out_line = line_inserter(i + 1, time_diffs);
rev_it_dist = std::distance(m_lines.rbegin(), rev_it) + 1;
m_lines.insert(rev_it.base(), { out_line, rev_it->time });
m_lines.insert(rev_it.base(), { out_line, rev_it->times });
#ifndef NDEBUG
m_statistics.add_line(out_line.length());
#endif // NDEBUG
@@ -3970,7 +3985,7 @@ void GCodeProcessor::post_process()
std::string out_string;
if (!m_lines.empty()) {
if (m_write_type == EWriteType::ByTime) {
while (m_lines.front().time < m_time - backtrace_time) {
while (m_lines.front().times[Normal] < m_times[Normal] - backtrace_time) {
const LineData& data = m_lines.front();
out_string += data.line;
m_size -= data.line.length();
@@ -4055,7 +4070,8 @@ void GCodeProcessor::post_process()
}
};
ExportLines export_lines(m_binarizer, m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]);
ExportLines export_lines(m_binarizer, m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize,
m_time_processor.machines);
// replace placeholder lines with the proper final value
// gcode_line is in/out parameter, to reduce expensive memory allocation
@@ -4259,9 +4275,14 @@ void GCodeProcessor::post_process()
}
export_lines.insert_lines(backtrace, cmd,
// line inserter
[tool_number, this](unsigned int id, float time, float time_diff) {
int temperature = int( m_layer_id != 1 ? m_extruder_temps_config[tool_number] : m_extruder_temps_first_layer_config[tool_number]);
const std::string out = "M104 T" + std::to_string(tool_number) + " P" + std::to_string(int(std::round(time_diff))) + " S" + std::to_string(temperature) + "\n";
[tool_number, this](unsigned int id, const std::vector<float>& time_diffs) {
const int temperature = int(m_layer_id != 1 ? m_extruder_temps_config[tool_number] : m_extruder_temps_first_layer_config[tool_number]);
std::string out = "M104.1 T" + std::to_string(tool_number);
if (time_diffs.size() > 0)
out += " P" + std::to_string(int(std::round(time_diffs[0])));
if (time_diffs.size() > 1)
out += " Q" + std::to_string(int(std::round(time_diffs[1])));
out += " S" + std::to_string(temperature) + "\n";
return out;
},
// line replacer

View File

@@ -198,6 +198,8 @@ public:
{ return { quantize(pt.x(), XYZF_EXPORT_DIGITS), quantize(pt.y(), XYZF_EXPORT_DIGITS) }; }
static Vec3d quantize(const Vec3d &pt)
{ return { quantize(pt.x(), XYZF_EXPORT_DIGITS), quantize(pt.y(), XYZF_EXPORT_DIGITS), quantize(pt.z(), XYZF_EXPORT_DIGITS) }; }
static Vec2d quantize(const Vec2f &pt)
{ return { quantize(double(pt.x()), XYZF_EXPORT_DIGITS), quantize(double(pt.y()), XYZF_EXPORT_DIGITS) }; }
void emit_axis(const char axis, const double v, size_t digits);

View File

@@ -1,10 +1,12 @@
#include "LabelObjects.hpp"
#include "ClipperUtils.hpp"
#include "GCode/GCodeWriter.hpp"
#include "Model.hpp"
#include "Print.hpp"
#include "TriangleMeshSlicer.hpp"
#include "boost/algorithm/string/replace.hpp"
namespace Slic3r::GCode {
@@ -39,10 +41,10 @@ Polygon instance_outline(const PrintInstance* pi)
}; // anonymous namespace
void LabelObjects::init(const Print& print)
void LabelObjects::init(const SpanOfConstPtrs<PrintObject>& objects, LabelObjectsStyle label_object_style, GCodeFlavor gcode_flavor)
{
m_label_objects_style = print.config().gcode_label_objects;
m_flavor = print.config().gcode_flavor;
m_label_objects_style = label_object_style;
m_flavor = gcode_flavor;
if (m_label_objects_style == LabelObjectsStyle::Disabled)
return;
@@ -51,7 +53,7 @@ void LabelObjects::init(const Print& print)
// Iterate over all PrintObjects and their PrintInstances, collect PrintInstances which
// belong to the same ModelObject.
for (const PrintObject* po : print.objects())
for (const PrintObject* po : objects)
for (const PrintInstance& pi : po->instances())
model_object_to_print_instances[pi.model_instance->get_object()].emplace_back(&pi);
@@ -87,13 +89,69 @@ void LabelObjects::init(const Print& print)
}
}
m_label_data.emplace(pi, LabelData{name, unique_id});
// Now calculate the polygon and center for Cancel Object (this is not always used).
Polygon outline = instance_outline(pi);
assert(! outline.empty());
outline.douglas_peucker(50000.f);
Point center = outline.centroid();
char buffer[64];
std::snprintf(buffer, sizeof(buffer) - 1, "%.3f,%.3f", unscale<float>(center[0]), unscale<float>(center[1]));
std::string center_str(buffer);
std::string polygon_str = std::string("[");
for (const Point& point : outline) {
std::snprintf(buffer, sizeof(buffer) - 1, "[%.3f,%.3f],", unscale<float>(point[0]), unscale<float>(point[1]));
polygon_str += buffer;
}
polygon_str.pop_back();
polygon_str += "]";
m_label_data.emplace_back(LabelData{pi, name, center_str, polygon_str, unique_id});
++unique_id;
}
}
}
bool LabelObjects::update(const PrintInstance *instance) {
if (this->last_operation_instance == instance) {
return false;
}
this->last_operation_instance = instance;
return true;
}
std::string LabelObjects::maybe_start_instance(GCodeWriter& writer) {
if (current_instance == nullptr && last_operation_instance != nullptr) {
current_instance = last_operation_instance;
std::string result{this->start_object(*current_instance, LabelObjects::IncludeName::No)};
result += writer.reset_e(true);
return result;
}
return "";
}
std::string LabelObjects::maybe_stop_instance() {
if (current_instance != nullptr) {
const std::string result{this->stop_object(*current_instance)};
current_instance = nullptr;
return result;
}
return "";
}
std::string LabelObjects::maybe_change_instance(GCodeWriter& writer) {
if (last_operation_instance != current_instance) {
const std::string stop_instance_gcode{this->maybe_stop_instance()};
// Be carefull with refactoring: this->maybe_stop_instance() + this->maybe_start_instance()
// may not be evaluated in order. The order is indeed undefined!
return stop_instance_gcode + this->maybe_start_instance(writer);
}
return "";
}
bool LabelObjects::has_active_instance() {
return this->current_instance != nullptr;
}
std::string LabelObjects::all_objects_header() const
{
@@ -102,38 +160,34 @@ std::string LabelObjects::all_objects_header() const
std::string out;
// Let's sort the values according to unique_id so they are in the same order in which they were added.
std::vector<std::pair<const PrintInstance*, LabelData>> label_data_sorted;
for (const auto& pi_and_label : m_label_data)
label_data_sorted.emplace_back(pi_and_label);
std::sort(label_data_sorted.begin(), label_data_sorted.end(), [](const auto& ld1, const auto& ld2) { return ld1.second.unique_id < ld2.second.unique_id; });
out += "\n";
for (const auto& [print_instance, label] : label_data_sorted) {
if (m_label_objects_style == LabelObjectsStyle::Firmware && m_flavor == gcfKlipper) {
char buffer[64];
out += "EXCLUDE_OBJECT_DEFINE NAME='" + label.name + "'";
Polygon outline = instance_outline(print_instance);
assert(! outline.empty());
outline.douglas_peucker(50000.f);
Point center = outline.centroid();
std::snprintf(buffer, sizeof(buffer) - 1, " CENTER=%.3f,%.3f", unscale<float>(center[0]), unscale<float>(center[1]));
out += buffer + std::string(" POLYGON=[");
for (const Point& point : outline) {
std::snprintf(buffer, sizeof(buffer) - 1, "[%.3f,%.3f],", unscale<float>(point[0]), unscale<float>(point[1]));
out += buffer;
}
out.pop_back();
out += "]\n";
} else {
out += start_object(*print_instance, IncludeName::Yes);
out += stop_object(*print_instance);
for (const LabelData& label : m_label_data) {
if (m_label_objects_style == LabelObjectsStyle::Firmware && m_flavor == gcfKlipper)
out += "EXCLUDE_OBJECT_DEFINE NAME='" + label.name + "' CENTER=" + label.center + " POLYGON=" + label.polygon + "\n";
else {
out += start_object(*label.pi, IncludeName::Yes);
out += stop_object(*label.pi);
}
}
out += "\n";
return out;
}
std::string LabelObjects::all_objects_header_singleline_json() const
{
std::string out;
out = "{\"objects\":[";
for (size_t i=0; i<m_label_data.size(); ++i) {
const LabelData& label = m_label_data[i];
out += std::string("{\"name\":\"") + label.name + "\",";
out += "\"polygon\":" + label.polygon + "}";
if (i != m_label_data.size() - 1)
out += ",";
}
out += "]}";
return out;
}
std::string LabelObjects::start_object(const PrintInstance& print_instance, IncludeName include_name) const
@@ -141,7 +195,7 @@ std::string LabelObjects::start_object(const PrintInstance& print_instance, Incl
if (m_label_objects_style == LabelObjectsStyle::Disabled)
return std::string();
const LabelData& label = m_label_data.at(&print_instance);
const LabelData& label = *std::find_if(m_label_data.begin(), m_label_data.end(), [&print_instance](const LabelData& ld) { return ld.pi == &print_instance; });
std::string out;
if (m_label_objects_style == LabelObjectsStyle::Octoprint)
@@ -170,7 +224,7 @@ std::string LabelObjects::stop_object(const PrintInstance& print_instance) const
if (m_label_objects_style == LabelObjectsStyle::Disabled)
return std::string();
const LabelData& label = m_label_data.at(&print_instance);
const LabelData& label = *std::find_if(m_label_data.begin(), m_label_data.end(), [&print_instance](const LabelData& ld) { return ld.pi == &print_instance; });
std::string out;
if (m_label_objects_style == LabelObjectsStyle::Octoprint)

View File

@@ -2,7 +2,9 @@
#define slic3r_GCode_LabelObjects_hpp_
#include <string>
#include <unordered_map>
#include <vector>
#include "libslic3r/Print.hpp"
namespace Slic3r {
@@ -11,30 +13,49 @@ enum class LabelObjectsStyle;
struct PrintInstance;
class Print;
class GCodeWriter;
namespace GCode {
class LabelObjects {
class LabelObjects
{
public:
void init(const SpanOfConstPtrs<PrintObject>& objects, LabelObjectsStyle label_object_style, GCodeFlavor gcode_flavor);
std::string all_objects_header() const;
std::string all_objects_header_singleline_json() const;
bool update(const PrintInstance *instance);
std::string maybe_start_instance(GCodeWriter& writer);
std::string maybe_stop_instance();
std::string maybe_change_instance(GCodeWriter& writer);
bool has_active_instance();
private:
struct LabelData
{
const PrintInstance* pi;
std::string name;
std::string center;
std::string polygon;
int unique_id;
};
enum class IncludeName {
No,
Yes
};
void init(const Print& print);
std::string all_objects_header() const;
std::string start_object(const PrintInstance& print_instance, IncludeName include_name) const;
std::string stop_object(const PrintInstance& print_instance) const;
private:
struct LabelData {
std::string name;
int unique_id;
};
const PrintInstance* current_instance{nullptr};
const PrintInstance* last_operation_instance{nullptr};
LabelObjectsStyle m_label_objects_style;
GCodeFlavor m_flavor;
std::unordered_map<const PrintInstance*, LabelData> m_label_data;
std::vector<LabelData> m_label_data;
};

View File

@@ -1,6 +1,7 @@
#include <memory.h>
#include <cstring>
#include <cfloat>
#include <algorithm>
#include "../libslic3r.h"
#include "../PrintConfig.hpp"
@@ -27,6 +28,11 @@ static constexpr float max_segment_length = 5.f;
// affect how distant will be propagated a flow rate adjustment.
static constexpr int max_look_back_limit = 128;
// Max non-extruding XY distance (travel move) in mm between two continuous extrusions where we pretend
// it's all one continuous extrusion line. Above this distance, we assume extruder pressure hits 0
// This exists because often there are tiny travel moves between stuff like infill.
// Lines where some extruder pressure will remain (so we should equalize between these small travels).
static constexpr double max_ignored_gap_between_extruding_segments = 3.;
PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_relative_e_distances(config.use_relative_e_distances.value)
{
// Preallocate some data, so that output_buffer.data() will return an empty string.
@@ -59,8 +65,8 @@ PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_
extrusion_rate_slope.positive = m_max_volumetric_extrusion_rate_slope_positive;
}
// Don't regulate the pressure before and after gap-fill and ironing.
for (const GCodeExtrusionRole er : {GCodeExtrusionRole::GapFill, GCodeExtrusionRole::Ironing}) {
// Don't regulate the pressure before and after ironing.
for (const GCodeExtrusionRole er : {GCodeExtrusionRole::Ironing}) {
m_max_volumetric_extrusion_rate_slopes[size_t(er)].negative = 0;
m_max_volumetric_extrusion_rate_slopes[size_t(er)].positive = 0;
}
@@ -97,6 +103,72 @@ void PressureEqualizer::process_layer(const std::string &gcode)
}
assert(!this->opened_extrude_set_speed_block);
}
// At this point, we have an entire layer of gcode lines loaded into m_gcode_lines.
// Now, we will split the mix of travels and extrusions into segments of continuous extrusions and process them.
// We skip over large travels, and pretend that small ones are part of a continuous extrusion segment.
for (auto current_extrusion_end_it = m_gcode_lines.cbegin(); current_extrusion_end_it != m_gcode_lines.cend();) {
// Find beginning of next extrusion segment from current position.
const auto current_extrusion_begin_it = std::find_if(current_extrusion_end_it, m_gcode_lines.cend(), [](const GCodeLine &line) {
return line.extruding();
});
// We start with extrusion length of zero.
current_extrusion_end_it = current_extrusion_begin_it;
// Inner loop extends the extrusion segment over small travel moves.
while (current_extrusion_end_it != m_gcode_lines.cend()) {
// Find the end of the current extrusion segment.
const auto travel_begin_it = std::find_if(std::next(current_extrusion_end_it), m_gcode_lines.cend(), [](const GCodeLine &line) {
return !line.extruding();
});
current_extrusion_end_it = std::prev(travel_begin_it);
const auto next_extrusion_segment_it = advance_segment_beyond_small_gap(current_extrusion_end_it);
if (std::distance(current_extrusion_end_it, next_extrusion_segment_it) > 0) {
// Extend the continuous line over the small gap.
current_extrusion_end_it = next_extrusion_segment_it;
continue; // Keep going, loop again to find the new end of extrusion segment.
} else {
break; // Gap to next extrude is too big, stop looking forward. We've found the end of this segment.
}
}
// Now, run the pressure equalizer across the segment like a streamroller.
// It operates on a sliding window that moves forward across gcode line by line.
const std::ptrdiff_t current_extrusion_begin_idx = std::distance(m_gcode_lines.cbegin(), current_extrusion_begin_it);
for (auto current_line_it = current_extrusion_begin_it; current_line_it != current_extrusion_end_it; ++current_line_it) {
const std::ptrdiff_t current_line_idx = std::distance(m_gcode_lines.cbegin(), current_line_it);
// Feed pressure equalizer past lines, going back to max_look_back_limit (or start of segment).
const size_t start_idx = size_t(std::max<std::ptrdiff_t>(current_extrusion_begin_idx, current_line_idx - max_look_back_limit));
adjust_volumetric_rate(start_idx, size_t(current_line_idx));
}
// Current extrusion is all done processing so advance beyond it for the next loop.
if (current_extrusion_end_it != m_gcode_lines.cend())
++current_extrusion_end_it;
}
}
PressureEqualizer::GCodeLinesConstIt PressureEqualizer::advance_segment_beyond_small_gap(const GCodeLinesConstIt &last_extruding_line_it) const {
// This should only be run on the last extruding line before a gap.
assert(last_extruding_line_it != m_gcode_lines.cend() && last_extruding_line_it->extruding());
double travel_distance = 0.;
// Start at the beginning of a gap, advance till extrusion found or gap too big.
for (auto current_line_it = std::next(last_extruding_line_it); current_line_it != m_gcode_lines.cend(); ++current_line_it) {
// Started extruding again! Return segment extension.
if (current_line_it->extruding())
return current_line_it;
travel_distance += current_line_it->dist_xy();
// Gap too big, don't extend segment.
if (travel_distance > max_ignored_gap_between_extruding_segments)
return last_extruding_line_it;
}
// Looped until the end of the layer and couldn't extend extrusion.
return last_extruding_line_it;
}
LayerResult PressureEqualizer::process_layer(LayerResult &&input)
@@ -391,7 +463,6 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo
buf.extruder_id = m_current_extruder;
memcpy(buf.pos_end, m_current_pos, sizeof(float)*5);
adjust_volumetric_rate();
#ifdef PRESSURE_EQUALIZER_DEBUG
++line_idx;
#endif
@@ -506,16 +577,14 @@ void PressureEqualizer::output_gcode_line(const size_t line_idx)
}
}
void PressureEqualizer::adjust_volumetric_rate()
void PressureEqualizer::adjust_volumetric_rate(const size_t first_line_idx, const size_t last_line_idx)
{
if (m_gcode_lines.size() < 2)
// Don't bother adjusting volumetric rate if there's no gcode to adjust.
if (last_line_idx <= first_line_idx || last_line_idx - first_line_idx < 2)
return;
// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes.
size_t fist_line_idx = size_t(std::max<int>(0, int(m_gcode_lines.size()) - max_look_back_limit));
const size_t last_line_idx = m_gcode_lines.size() - 1;
size_t line_idx = last_line_idx;
if (line_idx == fist_line_idx || !m_gcode_lines[line_idx].extruding())
if (line_idx == first_line_idx || !m_gcode_lines[line_idx].extruding())
// Nothing to do, the last move is not extruding.
return;
@@ -523,13 +592,13 @@ void PressureEqualizer::adjust_volumetric_rate()
feedrate_per_extrusion_role.fill(std::numeric_limits<float>::max());
feedrate_per_extrusion_role[int(m_gcode_lines[line_idx].extrusion_role)] = m_gcode_lines[line_idx].volumetric_extrusion_rate_start;
while (line_idx != fist_line_idx) {
while (line_idx != first_line_idx) {
size_t idx_prev = line_idx - 1;
for (; !m_gcode_lines[idx_prev].extruding() && idx_prev != fist_line_idx; --idx_prev);
for (; !m_gcode_lines[idx_prev].extruding() && idx_prev != first_line_idx; --idx_prev);
if (!m_gcode_lines[idx_prev].extruding())
break;
// Don't decelerate before ironing and gap-fill.
if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing || m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::GapFill) {
// Don't decelerate before ironing.
if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing) {
line_idx = idx_prev;
continue;
}
@@ -549,7 +618,8 @@ void PressureEqualizer::adjust_volumetric_rate()
// Limit by the succeeding volumetric flow rate.
rate_end = rate_succ;
if (!line.adjustable_flow || line.extrusion_role == GCodeExtrusionRole::ExternalPerimeter || line.extrusion_role == GCodeExtrusionRole::GapFill || line.extrusion_role == GCodeExtrusionRole::BridgeInfill || line.extrusion_role == GCodeExtrusionRole::Ironing) {
// Don't alter the flow rate for these extrusion types.
if (!line.adjustable_flow || line.extrusion_role == GCodeExtrusionRole::BridgeInfill || line.extrusion_role == GCodeExtrusionRole::Ironing) {
rate_end = line.volumetric_extrusion_rate_end;
} else if (line.volumetric_extrusion_rate_end > rate_end) {
line.volumetric_extrusion_rate_end = rate_end;
@@ -571,9 +641,8 @@ void PressureEqualizer::adjust_volumetric_rate()
line.modified = true;
}
}
// feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_start : rate_start;
// Don't store feed rate for ironing and gap-fill.
if (line.extrusion_role != GCodeExtrusionRole::Ironing && line.extrusion_role != GCodeExtrusionRole::GapFill)
// Don't store feed rate for ironing.
if (line.extrusion_role != GCodeExtrusionRole::Ironing)
feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_start;
}
}
@@ -587,8 +656,8 @@ void PressureEqualizer::adjust_volumetric_rate()
for (; !m_gcode_lines[idx_next].extruding() && idx_next != last_line_idx; ++idx_next);
if (!m_gcode_lines[idx_next].extruding())
break;
// Don't accelerate after ironing and gap-fill.
if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing || m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::GapFill) {
// Don't accelerate after ironing.
if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing) {
line_idx = idx_next;
continue;
}
@@ -603,7 +672,8 @@ void PressureEqualizer::adjust_volumetric_rate()
continue; // The positive rate is unlimited or the rate for GCodeExtrusionRole iRole is unlimited.
float rate_start = feedrate_per_extrusion_role[iRole];
if (!line.adjustable_flow || line.extrusion_role == GCodeExtrusionRole::ExternalPerimeter || line.extrusion_role == GCodeExtrusionRole::GapFill || line.extrusion_role == GCodeExtrusionRole::BridgeInfill || line.extrusion_role == GCodeExtrusionRole::Ironing) {
// Don't alter the flow rate for these extrusion types.
if (!line.adjustable_flow || line.extrusion_role == GCodeExtrusionRole::BridgeInfill || line.extrusion_role == GCodeExtrusionRole::Ironing) {
rate_start = line.volumetric_extrusion_rate_start;
} else if (iRole == size_t(line.extrusion_role) && rate_prec < rate_start)
rate_start = rate_prec;
@@ -627,9 +697,8 @@ void PressureEqualizer::adjust_volumetric_rate()
line.modified = true;
}
}
// feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_end : rate_end;
// Don't store feed rate for ironing and gap-fill.
if (line.extrusion_role != GCodeExtrusionRole::Ironing && line.extrusion_role != GCodeExtrusionRole::GapFill)
// Don't store feed rate for ironing
if (line.extrusion_role != GCodeExtrusionRole::Ironing)
feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_end;
}
}

View File

@@ -169,6 +169,8 @@ private:
bool extrude_end_tag = false;
};
using GCodeLines = std::vector<GCodeLine>;
using GCodeLinesConstIt = GCodeLines::const_iterator;
// Output buffer will only grow. It will not be reallocated over and over.
std::vector<char> output_buffer;
size_t output_buffer_length;
@@ -182,9 +184,10 @@ private:
bool process_line(const char *line, const char *line_end, GCodeLine &buf);
void output_gcode_line(size_t line_idx);
GCodeLinesConstIt advance_segment_beyond_small_gap(const GCodeLinesConstIt &last_extruding_line_it) const;
// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes.
// Then go forward and adjust the feedrate to decrease the slope of the extrusion rate changes.
void adjust_volumetric_rate();
void adjust_volumetric_rate(size_t first_line_idx, size_t last_line_idx);
// Push the text to the end of the output_buffer.
inline void push_to_output(GCodeG1Formatter &formatter);

View File

@@ -133,6 +133,11 @@ double clip_end(SmoothPath &path, double distance, double min_point_distance_thr
return distance;
}
void reverse(SmoothPath &path) {
std::reverse(path.begin(), path.end());
for (SmoothPathElement &path_element : path)
Geometry::ArcWelder::reverse(path_element.path);
}
void SmoothPathCache::interpolate_add(const ExtrusionPath &path, const InterpolationParameters &params)
{
double tolerance = params.tolerance;

View File

@@ -33,6 +33,7 @@ std::optional<Point> sample_path_point_at_distance_from_end(const SmoothPath &pa
// rather discard such a degenerate segment.
double clip_end(SmoothPath &path, double distance, double min_point_distance_threshold);
void reverse(SmoothPath &path);
class SmoothPathCache
{
public:

View File

@@ -1,10 +1,21 @@
#include "SpiralVase.hpp"
#include "GCode.hpp"
#include <sstream>
#include <cmath>
#include <limits>
namespace Slic3r {
std::string SpiralVase::process_layer(const std::string &gcode)
static AABBTreeLines::LinesDistancer<Linef> get_layer_distancer(const std::vector<Vec2f> &layer_points)
{
Linesf lines;
for (size_t idx = 1; idx < layer_points.size(); ++idx)
lines.emplace_back(layer_points[idx - 1].cast<double>(), layer_points[idx].cast<double>());
return AABBTreeLines::LinesDistancer{std::move(lines)};
}
std::string SpiralVase::process_layer(const std::string &gcode, bool last_layer)
{
/* This post-processor relies on several assumptions:
- all layers are processed through it, including those that are not supposed
@@ -22,8 +33,8 @@ std::string SpiralVase::process_layer(const std::string &gcode)
}
// Get total XY length for this layer by summing all extrusion moves.
float total_layer_length = 0;
float layer_height = 0;
float total_layer_length = 0.f;
float layer_height = 0.f;
float z = 0.f;
{
@@ -49,15 +60,21 @@ std::string SpiralVase::process_layer(const std::string &gcode)
// Remove layer height from initial Z.
z -= layer_height;
std::string new_gcode;
//FIXME Tapering of the transition layer only works reliably with relative extruder distances.
// FIXME Tapering of the transition layer and smoothing only works reliably with relative extruder distances.
// For absolute extruder distances it will be switched off.
// Tapering the absolute extruder distances requires to process every extrusion value after the first transition
// layer.
bool transition = m_transition_layer && m_config.use_relative_e_distances.value;
float layer_height_factor = layer_height / total_layer_length;
const bool transition_in = m_transition_layer && m_config.use_relative_e_distances.value;
const bool transition_out = last_layer && m_config.use_relative_e_distances.value;
const bool smooth_spiral = m_smooth_spiral && m_config.use_relative_e_distances.value;
const AABBTreeLines::LinesDistancer previous_layer_distancer = get_layer_distancer(m_previous_layer);
Vec2f last_point = m_previous_layer.empty() ? Vec2f::Zero() : m_previous_layer.back();
float len = 0.f;
m_reader.parse_buffer(gcode, [&new_gcode, &z, total_layer_length, layer_height_factor, transition, &len]
std::string new_gcode, transition_gcode;
std::vector<Vec2f> current_layer;
m_reader.parse_buffer(gcode, [z, total_layer_length, layer_height, transition_in, transition_out, smooth_spiral, max_xy_smoothing = m_max_xy_smoothing,
&len, &last_point, &new_gcode, &transition_gcode, &current_layer, &previous_layer_distancer]
(GCodeReader &reader, GCodeReader::GCodeLine line) {
if (line.cmd_is("G1")) {
if (line.has_z()) {
@@ -66,16 +83,52 @@ std::string SpiralVase::process_layer(const std::string &gcode)
line.set(reader, Z, z);
new_gcode += line.raw() + '\n';
return;
} else if (line.has_x() || line.has_y()) { // Sometimes lines have X/Y but the move is to the last position.
if (const float dist_XY = line.dist_XY(reader); dist_XY > 0 && line.extruding(reader)) { // Exclude wipe and retract
len += dist_XY;
const float factor = len / total_layer_length;
if (transition_in)
// Transition layer, interpolate the amount of extrusion from zero to the final value.
line.set(reader, E, line.e() * factor, 5);
else if (transition_out) {
// We want the last layer to ramp down extrusion, but without changing z height!
// So clone the line before we mess with its Z and duplicate it into a new layer that ramps down E
// We add this new layer at the very end
GCodeReader::GCodeLine transition_line(line);
transition_line.set(reader, E, line.e() * (1.f - factor), 5);
transition_gcode += transition_line.raw() + '\n';
}
// This line is the core of Spiral Vase mode, ramp up the Z smoothly
line.set(reader, Z, z + factor * layer_height);
bool emit_gcode_line = true;
if (smooth_spiral) {
// Now we also need to try to interpolate X and Y
Vec2f p(line.x(), line.y()); // Get current x/y coordinates
current_layer.emplace_back(p); // Store that point for later use on the next layer
auto [nearest_distance, idx, nearest_pt] = previous_layer_distancer.distance_from_lines_extra<false>(p.cast<double>());
if (nearest_distance < max_xy_smoothing) {
// Interpolate between the point on this layer and the point on the previous layer
Vec2f target = nearest_pt.cast<float>() * (1.f - factor) + p * factor;
// We will emit a new g-code line only when XYZ positions differ from the previous g-code line.
emit_gcode_line = GCodeFormatter::quantize(last_point) != GCodeFormatter::quantize(target);
line.set(reader, X, target.x());
line.set(reader, Y, target.y());
// We need to figure out the distance of this new line!
float modified_dist_XY = (last_point - target).norm();
// Scale the extrusion amount according to change in length
line.set(reader, E, line.e() * modified_dist_XY / dist_XY, 5);
last_point = target;
} else {
float dist_XY = line.dist_XY(reader);
if (dist_XY > 0) {
// horizontal move
if (line.extruding(reader)) {
len += dist_XY;
line.set(reader, Z, z + len * layer_height_factor);
if (transition && line.has(E))
// Transition layer, modulate the amount of extrusion from zero to the final value.
line.set(reader, E, line.value(E) * len / total_layer_length);
last_point = p;
}
}
if (emit_gcode_line)
new_gcode += line.raw() + '\n';
}
return;
@@ -84,14 +137,18 @@ std::string SpiralVase::process_layer(const std::string &gcode)
cause a visible seam when loops are not aligned in XY; by skipping
it we blend the first loop move in the XY plane (although the smoothness
of such blend depend on how long the first segment is; maybe we should
enforce some minimum length?). */
enforce some minimum length?).
When smooth_spiral is enabled, we're gonna end up exactly where the next layer should
start anyway, so we don't need the travel move */
}
}
}
new_gcode += line.raw() + '\n';
if (transition_out)
transition_gcode += line.raw() + '\n';
});
return new_gcode;
m_previous_layer = std::move(current_layer);
return new_gcode + transition_gcode;
}
}

View File

@@ -6,28 +6,38 @@
namespace Slic3r {
class SpiralVase {
class SpiralVase
{
public:
SpiralVase(const PrintConfig &config) : m_config(config)
SpiralVase() = delete;
explicit SpiralVase(const PrintConfig &config) : m_config(config)
{
m_reader.z() = (float)m_config.z_offset;
m_reader.apply_config(m_config);
const double max_nozzle_diameter = *std::max_element(config.nozzle_diameter.values.begin(), config.nozzle_diameter.values.end());
m_max_xy_smoothing = float(2. * max_nozzle_diameter);
};
void enable(bool en) {
m_transition_layer = en && ! m_enabled;
m_enabled = en;
void enable(bool enable)
{
m_transition_layer = enable && !m_enabled;
m_enabled = enable;
}
std::string process_layer(const std::string &gcode);
std::string process_layer(const std::string &gcode, bool last_layer);
private:
const PrintConfig &m_config;
GCodeReader m_reader;
float m_max_xy_smoothing = 0.f;
bool m_enabled = false;
// First spiral vase layer. Layer height has to be ramped up from zero to the target layer height.
bool m_transition_layer = false;
// Whether to interpolate XY coordinates with the previous layer. Results in no seam at layer changes
bool m_smooth_spiral = true;
std::vector<Vec2f> m_previous_layer;
};
}

View File

@@ -32,27 +32,11 @@ void Wipe::init(const PrintConfig &config, const std::vector<unsigned int> &extr
this->enable(wipe_xy);
}
void Wipe::set_path(SmoothPath &&path, bool reversed)
{
void Wipe::set_path(SmoothPath &&path) {
this->reset_path();
if (this->enabled() && ! path.empty()) {
if (coord_t wipe_len_max_scaled = scaled(m_wipe_len_max); reversed) {
m_path = std::move(path.back().path);
Geometry::ArcWelder::reverse(m_path);
int64_t len = Geometry::ArcWelder::estimate_path_length(m_path);
for (auto it = std::next(path.rbegin()); len < wipe_len_max_scaled && it != path.rend(); ++ it) {
if (it->path_attributes.role.is_bridge())
break; // Do not perform a wipe on bridges.
assert(it->path.size() >= 2);
assert(m_path.back().point == it->path.back().point);
if (m_path.back().point != it->path.back().point)
// ExtrusionMultiPath is interrupted in some place. This should not really happen.
break;
len += Geometry::ArcWelder::estimate_path_length(it->path);
m_path.insert(m_path.end(), it->path.rbegin() + 1, it->path.rend());
}
} else {
const coord_t wipe_len_max_scaled = scaled(m_wipe_len_max);
m_path = std::move(path.front().path);
int64_t len = Geometry::ArcWelder::estimate_path_length(m_path);
for (auto it = std::next(path.begin()); len < wipe_len_max_scaled && it != path.end(); ++ it) {
@@ -67,7 +51,6 @@ void Wipe::set_path(SmoothPath &&path, bool reversed)
m_path.insert(m_path.end(), it->path.begin() + 1, it->path.end());
}
}
}
assert(m_path.empty() || m_path.size() > 1);
}

View File

@@ -42,7 +42,7 @@ public:
if (this->enabled() && path.size() > 1)
m_path = std::move(path);
}
void set_path(SmoothPath &&path, bool reversed);
void set_path(SmoothPath &&path);
void offset_path(const Point &v) { m_offset += v; }
std::string wipe(GCodeGenerator &gcodegen, bool toolchange);

View File

@@ -22,6 +22,16 @@
namespace Slic3r
{
// Calculates length of extrusion line to extrude given volume
static float volume_to_length(float volume, float line_width, float layer_height)
{
return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
}
static float length_to_volume(float length, float line_width, float layer_height)
{
return std::max(0.f, length * layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f)));
}
class WipeTowerWriter
{
public:
@@ -102,6 +112,10 @@ public:
return *this;
}
WipeTowerWriter& switch_filament_monitoring(bool enable) {
m_gcode += std::string("G4 S0\n") + "M591 " + (enable ? "R" : "S0") + "\n";
return *this;
}
// Suppress / resume G-code preview in Slic3r. Slic3r will have difficulty to differentiate the various
// filament loading and cooling moves from normal extrusion moves. Therefore the writer
// is asked to suppres output of some lines, which look like extrusions.
@@ -284,6 +298,24 @@ public:
return extrude_explicit(end_point, y(), loading_dist, x_speed * 60.f, false, false);
}
// Loads filament while also moving towards given point in x-axis. Unlike the previous function, this one respects
// both the loading_speed and x_speed. Can shorten the move.
WipeTowerWriter& load_move_x_advanced_there_and_back(float farthest_x, float e_dist, float e_speed, float x_speed)
{
float old_x = x();
float time = std::abs(e_dist / e_speed); // time that the whole move must take
float x_max_dist = std::abs(farthest_x - x()); // max x-distance that we can travel
float x_dist = x_speed * time; // totel x-distance to travel during the move
int n = int(x_dist / (2*x_max_dist) + 1.f); // how many there and back moves should we do
float r = 2*n*x_max_dist / x_dist; // actual/required dist if the move is not shortened
float end_point = x() + (farthest_x > x() ? 1.f : -1.f) * x_max_dist / r;
for (int i=0; i<n; ++i) {
extrude_explicit(end_point, y(), e_dist/(2.f*n), x_speed * 60.f, false, false);
extrude_explicit(old_x, y(), e_dist/(2.f*n), x_speed * 60.f, false, false);
}
return *this;
}
// Elevate the extruder head above the current print_z position.
WipeTowerWriter& z_hop(float hop, float f = 0.f)
{
@@ -326,6 +358,7 @@ public:
// Set extruder temperature, don't wait by default.
WipeTowerWriter& set_extruder_temp(int temperature, bool wait = false)
{
m_gcode += "G4 S0\n"; // to flush planner queue
m_gcode += "M" + std::to_string(wait ? 109 : 104) + " S" + std::to_string(temperature) + "\n";
return *this;
}
@@ -523,7 +556,9 @@ WipeTower::WipeTower(const PrintConfig& config, const PrintRegionConfig& default
m_wipe_tower_rotation_angle(float(config.wipe_tower_rotation_angle)),
m_wipe_tower_brim_width(float(config.wipe_tower_brim_width)),
m_wipe_tower_cone_angle(float(config.wipe_tower_cone_angle)),
m_extra_spacing(float(config.wipe_tower_extra_spacing/100.)),
m_extra_flow(float(config.wipe_tower_extra_flow/100.)),
m_extra_spacing_wipe(float(config.wipe_tower_extra_spacing/100. * config.wipe_tower_extra_flow/100.)),
m_extra_spacing_ramming(float(config.wipe_tower_extra_spacing/100.)),
m_y_shift(0.f),
m_z_pos(0.f),
m_bridging(float(config.wipe_tower_bridging)),
@@ -560,6 +595,7 @@ WipeTower::WipeTower(const PrintConfig& config, const PrintRegionConfig& default
m_set_extruder_trimpot = config.high_current_on_filament_swap;
}
m_is_mk4mmu3 = boost::icontains(config.printer_notes.value, "PRINTER_MODEL_MK4") && boost::icontains(config.printer_notes.value, "MMU");
// Calculate where the priming lines should be - very naive test not detecting parallelograms etc.
const std::vector<Vec2d>& bed_points = config.bed_shape.values;
BoundingBoxf bb(bed_points);
@@ -594,6 +630,7 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config)
m_filpar[idx].is_soluble = config.wipe_tower_extruder == 0 ? config.filament_soluble.get_at(idx) : (idx != size_t(config.wipe_tower_extruder - 1));
m_filpar[idx].temperature = config.temperature.get_at(idx);
m_filpar[idx].first_layer_temperature = config.first_layer_temperature.get_at(idx);
m_filpar[idx].filament_minimal_purge_on_wipe_tower = config.filament_minimal_purge_on_wipe_tower.get_at(idx);
// If this is a single extruder MM printer, we will use all the SE-specific config values.
// Otherwise, the defaults will be used to turn off the SE stuff.
@@ -606,6 +643,8 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config)
m_filpar[idx].cooling_moves = config.filament_cooling_moves.get_at(idx);
m_filpar[idx].cooling_initial_speed = float(config.filament_cooling_initial_speed.get_at(idx));
m_filpar[idx].cooling_final_speed = float(config.filament_cooling_final_speed.get_at(idx));
m_filpar[idx].filament_stamping_loading_speed = float(config.filament_stamping_loading_speed.get_at(idx));
m_filpar[idx].filament_stamping_distance = float(config.filament_stamping_distance.get_at(idx));
}
m_filpar[idx].filament_area = float((M_PI/4.f) * pow(config.filament_diameter.get_at(idx), 2)); // all extruders are assumed to have the same filament diameter at this point
@@ -719,7 +758,7 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
toolchange_Wipe(writer, cleaning_box , 20.f);
box_coordinates box = cleaning_box;
box.translate(0.f, writer.y() - cleaning_box.ld.y() + m_perimeter_width);
toolchange_Unload(writer, box , m_filpar[m_current_tool].material, m_filpar[tools[idx_tool + 1]].first_layer_temperature);
toolchange_Unload(writer, box , m_filpar[m_current_tool].material, m_filpar[m_current_tool].first_layer_temperature, m_filpar[tools[idx_tool + 1]].first_layer_temperature);
cleaning_box.translate(prime_section_width, 0.f);
writer.travel(cleaning_box.ld, 7200);
}
@@ -766,7 +805,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool)
for (const auto &b : m_layer_info->tool_changes)
if ( b.new_tool == tool ) {
wipe_volume = b.wipe_volume;
wipe_area = b.required_depth * m_layer_info->extra_spacing;
wipe_area = b.required_depth;
break;
}
}
@@ -807,14 +846,15 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool)
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
if (tool != (unsigned int)-1){ // This is not the last change.
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material,
is_first_layer() ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature);
(is_first_layer() ? m_filpar[m_current_tool].first_layer_temperature : m_filpar[m_current_tool].temperature),
(is_first_layer() ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature));
toolchange_Change(writer, tool, m_filpar[tool].material); // Change the tool, set a speed override for soluble and flex materials.
toolchange_Load(writer, cleaning_box);
writer.travel(writer.x(), writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road
toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area.
++ m_num_tool_changes;
} else
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature, m_filpar[m_current_tool].temperature);
m_depth_traversed += wipe_area;
@@ -841,13 +881,14 @@ void WipeTower::toolchange_Unload(
WipeTowerWriter &writer,
const box_coordinates &cleaning_box,
const std::string& current_material,
const int old_temperature,
const int new_temperature)
{
float xl = cleaning_box.ld.x() + 1.f * m_perimeter_width;
float xr = cleaning_box.rd.x() - 1.f * m_perimeter_width;
const float line_width = m_perimeter_width * m_filpar[m_current_tool].ramming_line_width_multiplicator; // desired ramming line thickness
const float y_step = line_width * m_filpar[m_current_tool].ramming_step_multiplicator * m_extra_spacing; // spacing between lines in mm
const float y_step = line_width * m_filpar[m_current_tool].ramming_step_multiplicator * m_extra_spacing_ramming; // spacing between lines in mm
const Vec2f ramming_start_pos = Vec2f(xl, cleaning_box.ld.y() + m_depth_traversed + y_step/2.f);
@@ -860,10 +901,14 @@ void WipeTower::toolchange_Unload(
float e_done = 0; // measures E move done from each segment
const bool do_ramming = m_semm || m_filpar[m_current_tool].multitool_ramming;
const bool cold_ramming = m_is_mk4mmu3;
if (do_ramming) {
writer.travel(ramming_start_pos); // move to starting position
if (! m_is_mk4mmu3)
writer.disable_linear_advance();
if (cold_ramming)
writer.set_extruder_temp(old_temperature - 20);
}
else
writer.set_position(ramming_start_pos);
@@ -884,7 +929,7 @@ void WipeTower::toolchange_Unload(
if (tch.old_tool == m_current_tool) {
sum_of_depths += tch.ramming_depth;
float ramming_end_y = sum_of_depths;
ramming_end_y -= (y_step/m_extra_spacing-m_perimeter_width) / 2.f; // center of final ramming line
ramming_end_y -= (y_step/m_extra_spacing_ramming-m_perimeter_width) / 2.f; // center of final ramming line
if ( (m_current_shape == SHAPE_REVERSED && ramming_end_y < sparse_beginning_y - 0.5f*m_perimeter_width ) ||
(m_current_shape == SHAPE_NORMAL && ramming_end_y > sparse_beginning_y + 0.5f*m_perimeter_width ) )
@@ -898,6 +943,10 @@ void WipeTower::toolchange_Unload(
}
}
if (m_is_mk4mmu3) {
writer.switch_filament_monitoring(false);
writer.wait(1.5f);
}
// now the ramming itself:
while (do_ramming && i < m_filpar[m_current_tool].ramming_speed.size())
@@ -938,31 +987,66 @@ void WipeTower::toolchange_Unload(
.retract(0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed * 60.f)
.resume_preview();
}
const int& number_of_cooling_moves = m_filpar[m_current_tool].cooling_moves;
const bool cooling_will_happen = m_semm && number_of_cooling_moves > 0;
bool change_temp_later = false;
// Wipe tower should only change temperature with single extruder MM. Otherwise, all temperatures should
// be already set and there is no need to change anything. Also, the temperature could be changed
// for wrong extruder.
if (m_semm) {
if (new_temperature != 0 && (new_temperature != m_old_temperature || is_first_layer()) ) { // Set the extruder temperature, but don't wait.
if (new_temperature != 0 && (new_temperature != m_old_temperature || is_first_layer() || cold_ramming) ) { // Set the extruder temperature, but don't wait.
// If the required temperature is the same as last time, don't emit the M104 again (if user adjusted the value, it would be reset)
// However, always change temperatures on the first layer (this is to avoid issues with priming lines turned off).
if (cold_ramming && cooling_will_happen)
change_temp_later = true;
else
writer.set_extruder_temp(new_temperature, false);
m_old_temperature = new_temperature;
}
}
// Cooling:
const int& number_of_moves = m_filpar[m_current_tool].cooling_moves;
if (m_semm && number_of_moves > 0) {
if (cooling_will_happen) {
const float& initial_speed = m_filpar[m_current_tool].cooling_initial_speed;
const float& final_speed = m_filpar[m_current_tool].cooling_final_speed;
float speed_inc = (final_speed - initial_speed) / (2.f * number_of_moves - 1.f);
float speed_inc = (final_speed - initial_speed) / (2.f * number_of_cooling_moves - 1.f);
if (m_is_mk4mmu3)
writer.disable_linear_advance();
writer.suppress_preview()
.travel(writer.x(), writer.y() + y_step);
old_x = writer.x();
turning_point = xr-old_x > old_x-xl ? xr : xl;
for (int i=0; i<number_of_moves; ++i) {
float stamping_dist_e = m_filpar[m_current_tool].filament_stamping_distance + m_cooling_tube_length / 2.f;
for (int i=0; i<number_of_cooling_moves; ++i) {
// Stamping - happens after every cooling move except for the last one.
if (i>0 && m_filpar[m_current_tool].filament_stamping_distance != 0) {
// Stamping turning point shall be no farther than 20mm from the current nozzle position:
float stamping_turning_point = std::clamp(old_x + 20.f * (turning_point - old_x > 0.f ? 1.f : -1.f), xl, xr);
// Only last 5mm will be done with the fast x travel. The point is to spread possible blobs
// along the whole wipe tower.
if (stamping_dist_e > 5) {
float cent = writer.x();
writer.load_move_x_advanced(stamping_turning_point, (stamping_dist_e - 5), m_filpar[m_current_tool].filament_stamping_loading_speed, 200);
writer.load_move_x_advanced(cent, 5, m_filpar[m_current_tool].filament_stamping_loading_speed, m_travel_speed);
writer.travel(cent, writer.y());
} else
writer.load_move_x_advanced_there_and_back(stamping_turning_point, stamping_dist_e, m_filpar[m_current_tool].filament_stamping_loading_speed, m_travel_speed);
// Retract while the print head is stationary, so if there is a blob, it is not dragged along.
writer.retract(stamping_dist_e, m_filpar[m_current_tool].unloading_speed * 60.f);
}
if (i == number_of_cooling_moves - 1 && change_temp_later) {
// If cold_ramming, the temperature change should be done before the last cooling move.
writer.set_extruder_temp(new_temperature, false);
}
float speed = initial_speed + speed_inc * 2*i;
writer.load_move_x_advanced(turning_point, m_cooling_tube_length, speed);
speed += speed_inc;
@@ -979,7 +1063,7 @@ void WipeTower::toolchange_Unload(
// this is to align ramming and future wiping extrusions, so the future y-steps can be uniform from the start:
// the perimeter_width will later be subtracted, it is there to not load while moving over just extruded material
Vec2f pos = Vec2f(end_of_ramming.x(), end_of_ramming.y() + (y_step/m_extra_spacing-m_perimeter_width) / 2.f + m_perimeter_width);
Vec2f pos = Vec2f(end_of_ramming.x(), end_of_ramming.y() + (y_step/m_extra_spacing_ramming-m_perimeter_width) / 2.f + m_perimeter_width);
if (do_ramming)
writer.travel(pos, 2400.f);
else
@@ -1004,6 +1088,8 @@ void WipeTower::toolchange_Change(
//writer.append("[end_filament_gcode]\n");
writer.append("[toolchange_gcode_from_wipe_tower_generator]\n");
if (m_is_mk4mmu3)
writer.switch_filament_monitoring(true);
// Travel to where we assume we are. Custom toolchange or some special T code handling (parking extruder etc)
// gcode could have left the extruder somewhere, we cannot just start extruding. We should also inform the
// postprocessor that we absolutely want to have this in the gcode, even if it thought it is the same as before.
@@ -1064,20 +1150,23 @@ void WipeTower::toolchange_Wipe(
const float& xl = cleaning_box.ld.x();
const float& xr = cleaning_box.rd.x();
writer.set_extrusion_flow(m_extrusion_flow * m_extra_flow);
const float line_width = m_perimeter_width * m_extra_flow;
writer.change_analyzer_line_width(line_width);
// Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least
// the ordered volume, even if it means violating the box. This can later be removed and simply
// wipe until the end of the assigned area.
float x_to_wipe = volume_to_length(wipe_volume, m_perimeter_width, m_layer_height) * (is_first_layer() ? m_extra_spacing : 1.f);
float dy = (is_first_layer() ? 1.f : m_extra_spacing) * m_perimeter_width; // Don't use the extra spacing for the first layer.
float x_to_wipe = volume_to_length(wipe_volume, m_perimeter_width, m_layer_height) / m_extra_flow;
float dy = (is_first_layer() ? m_extra_flow : m_extra_spacing_wipe) * m_perimeter_width; // Don't use the extra spacing for the first layer, but do use the spacing resulting from increased flow.
// All the calculations in all other places take the spacing into account for all the layers.
const float target_speed = is_first_layer() ? m_first_layer_speed * 60.f : m_infill_speed * 60.f;
float wipe_speed = 0.33f * target_speed;
// if there is less than 2.5*m_perimeter_width to the edge, advance straightaway (there is likely a blob anyway)
if ((m_left_to_right ? xr-writer.x() : writer.x()-xl) < 2.5f*m_perimeter_width) {
writer.travel((m_left_to_right ? xr-m_perimeter_width : xl+m_perimeter_width),writer.y()+dy);
// if there is less than 2.5*line_width to the edge, advance straightaway (there is likely a blob anyway)
if ((m_left_to_right ? xr-writer.x() : writer.x()-xl) < 2.5f*line_width) {
writer.travel((m_left_to_right ? xr-line_width : xl+line_width),writer.y()+dy);
m_left_to_right = !m_left_to_right;
}
@@ -1092,21 +1181,21 @@ void WipeTower::toolchange_Wipe(
float traversed_x = writer.x();
if (m_left_to_right)
writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5f*m_perimeter_width), writer.y(), wipe_speed);
writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5f*line_width), writer.y(), wipe_speed);
else
writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5f*m_perimeter_width), writer.y(), wipe_speed);
writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5f*line_width), writer.y(), wipe_speed);
if (writer.y()+float(EPSILON) > cleaning_box.lu.y()-0.5f*m_perimeter_width)
if (writer.y()+float(EPSILON) > cleaning_box.lu.y()-0.5f*line_width)
break; // in case next line would not fit
traversed_x -= writer.x();
x_to_wipe -= std::abs(traversed_x);
if (x_to_wipe < WT_EPSILON) {
writer.travel(m_left_to_right ? xl + 1.5f*m_perimeter_width : xr - 1.5f*m_perimeter_width, writer.y(), 7200);
writer.travel(m_left_to_right ? xl + 1.5f*line_width : xr - 1.5f*line_width, writer.y(), 7200);
break;
}
// stepping to the next line:
writer.extrude(writer.x() + (i % 4 == 0 ? -1.f : (i % 4 == 1 ? 1.f : 0.f)) * 1.5f*m_perimeter_width, writer.y() + dy);
writer.extrude(writer.x() + (i % 4 == 0 ? -1.f : (i % 4 == 1 ? 1.f : 0.f)) * 1.5f*line_width, writer.y() + dy);
m_left_to_right = !m_left_to_right;
}
@@ -1120,6 +1209,7 @@ void WipeTower::toolchange_Wipe(
m_left_to_right = !m_left_to_right;
writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow.
writer.change_analyzer_line_width(m_perimeter_width);
}
@@ -1399,9 +1489,19 @@ std::vector<std::vector<float>> WipeTower::extract_wipe_volumes(const PrintConfi
// Extract purging volumes for each extruder pair:
std::vector<std::vector<float>> wipe_volumes;
const unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+EPSILON);
for (unsigned int i = 0; i<number_of_extruders; ++i)
for (size_t i = 0; i<number_of_extruders; ++i)
wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders));
// For SEMM printers, the project can be configured to use defaults from configuration,
// in which case the custom matrix shall be ignored. We will overwrite the values.
if (config.single_extruder_multi_material && ! config.wiping_volumes_use_custom_matrix) {
for (size_t i = 0; i < number_of_extruders; ++i) {
for (size_t j = 0; j < number_of_extruders; ++j) {
if (i != j)
wipe_volumes[i][j] = (i == j ? 0.f : config.multimaterial_purging.value * config.filament_purge_multiplier.get_at(j) / 100.f);
}
}
}
// Also include filament_minimal_purge_on_wipe_tower. This is needed for the preview.
for (unsigned int i = 0; i<number_of_extruders; ++i)
for (unsigned int j = 0; j<number_of_extruders; ++j)
@@ -1410,6 +1510,13 @@ std::vector<std::vector<float>> WipeTower::extract_wipe_volumes(const PrintConfi
return wipe_volumes;
}
static float get_wipe_depth(float volume, float layer_height, float perimeter_width, float extra_flow, float extra_spacing, float width)
{
float length_to_extrude = (volume_to_length(volume, perimeter_width, layer_height)) / extra_flow;
length_to_extrude = std::max(length_to_extrude,0.f);
return (int(length_to_extrude / width) + 1) * perimeter_width * extra_spacing;
}
// Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box
void WipeTower::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool,
unsigned int new_tool, float wipe_volume)
@@ -1426,22 +1533,17 @@ void WipeTower::plan_toolchange(float z_par, float layer_height_par, unsigned in
return;
// this is an actual toolchange - let's calculate depth to reserve on the wipe tower
float depth = 0.f;
float width = m_wipe_tower_width - 3*m_perimeter_width;
float length_to_extrude = volume_to_length(0.25f * std::accumulate(m_filpar[old_tool].ramming_speed.begin(), m_filpar[old_tool].ramming_speed.end(), 0.f),
m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator,
layer_height_par);
depth = (int(length_to_extrude / width) + 1) * (m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator * m_filpar[old_tool].ramming_step_multiplicator);
float ramming_depth = depth;
length_to_extrude = width*((length_to_extrude / width)-int(length_to_extrude / width)) - width;
float first_wipe_line = -length_to_extrude;
length_to_extrude += volume_to_length(wipe_volume, m_perimeter_width, layer_height_par);
length_to_extrude = std::max(length_to_extrude,0.f);
float ramming_depth = (int(length_to_extrude / width) + 1) * (m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator * m_filpar[old_tool].ramming_step_multiplicator) * m_extra_spacing_ramming;
float first_wipe_line = - (width*((length_to_extrude / width)-int(length_to_extrude / width)) - width);
depth += (int(length_to_extrude / width) + 1) * m_perimeter_width;
depth *= m_extra_spacing;
float first_wipe_volume = length_to_volume(first_wipe_line, m_perimeter_width * m_extra_flow, layer_height_par);
float wiping_depth = get_wipe_depth(wipe_volume - first_wipe_volume, layer_height_par, m_perimeter_width, m_extra_flow, m_extra_spacing_wipe, width);
m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth, ramming_depth, first_wipe_line, wipe_volume));
m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, ramming_depth + wiping_depth, ramming_depth, first_wipe_line, wipe_volume));
}
@@ -1491,14 +1593,14 @@ void WipeTower::save_on_last_wipe()
if (i == idx) {
float width = m_wipe_tower_width - 3*m_perimeter_width; // width we draw into
float length_to_save = finish_layer().total_extrusion_length_in_plane();
float length_to_wipe = volume_to_length(toolchange.wipe_volume,
m_perimeter_width, m_layer_info->height) - toolchange.first_wipe_line - length_to_save;
length_to_wipe = std::max(length_to_wipe,0.f);
float depth_to_wipe = m_perimeter_width * (std::floor(length_to_wipe/width) + ( length_to_wipe > 0.f ? 1.f : 0.f ) );
float volume_to_save = length_to_volume(finish_layer().total_extrusion_length_in_plane(), m_perimeter_width, m_layer_info->height);
float volume_left_to_wipe = std::max(m_filpar[toolchange.new_tool].filament_minimal_purge_on_wipe_tower, toolchange.wipe_volume_total - volume_to_save);
float volume_we_need_depth_for = std::max(0.f, volume_left_to_wipe - length_to_volume(toolchange.first_wipe_line, m_perimeter_width*m_extra_flow, m_layer_info->height));
float depth_to_wipe = get_wipe_depth(volume_we_need_depth_for, m_layer_info->height, m_perimeter_width, m_extra_flow, m_extra_spacing_wipe, width);
toolchange.required_depth = (toolchange.ramming_depth + depth_to_wipe) * m_extra_spacing;
toolchange.required_depth = toolchange.ramming_depth + depth_to_wipe;
toolchange.wipe_volume = volume_left_to_wipe;
}
}
}

View File

@@ -1,6 +1,5 @@
#ifndef WipeTower_
#define WipeTower_
#ifndef slic3r_GCode_WipeTower_hpp_
#define slic3r_GCode_WipeTower_hpp_
#include <cmath>
#include <string>
#include <sstream>
@@ -232,6 +231,8 @@ public:
float unloading_speed = 0.f;
float unloading_speed_start = 0.f;
float delay = 0.f ;
float filament_stamping_loading_speed = 0.f;
float filament_stamping_distance = 0.f;
int cooling_moves = 0;
float cooling_initial_speed = 0.f;
float cooling_final_speed = 0.f;
@@ -243,6 +244,7 @@ public:
float filament_area;
bool multitool_ramming;
float multitool_ramming_time = 0.f;
float filament_minimal_purge_on_wipe_tower = 0.f;
};
private:
@@ -260,6 +262,7 @@ private:
bool m_semm = true; // Are we using a single extruder multimaterial printer?
bool m_is_mk4mmu3 = false;
Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
float m_wipe_tower_width; // Width of the wipe tower.
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
@@ -309,8 +312,6 @@ private:
// State of the wipe tower generator.
unsigned int m_num_layer_changes = 0; // Layer change counter for the output statistics.
unsigned int m_num_tool_changes = 0; // Tool change change counter for the output statistics.
///unsigned int m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes.
bool m_print_brim = true;
// A fill-in direction (positive Y, negative Y) alternates with each layer.
wipe_shape m_current_shape = SHAPE_NORMAL;
size_t m_current_tool = 0;
@@ -319,7 +320,9 @@ private:
float m_depth_traversed = 0.f; // Current y position at the wipe tower.
bool m_current_layer_finished = false;
bool m_left_to_right = true;
float m_extra_spacing = 1.f;
float m_extra_flow = 1.f;
float m_extra_spacing_wipe = 1.f;
float m_extra_spacing_ramming = 1.f;
bool is_first_layer() const { return size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx; }
@@ -331,16 +334,10 @@ private:
return layer_height * ( m_perimeter_width - layer_height * (1.f-float(M_PI)/4.f)) / filament_area();
}
// Calculates length of extrusion line to extrude given volume
float volume_to_length(float volume, float line_width, float layer_height) const {
return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
}
// Calculates depth for all layers and propagates them downwards
void plan_tower();
// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
void make_wipe_tower_square();
// Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
void save_on_last_wipe();
@@ -355,19 +352,19 @@ private:
float ramming_depth;
float first_wipe_line;
float wipe_volume;
float wipe_volume_total;
ToolChange(size_t old, size_t newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f)
: old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv} {}
: old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv}, wipe_volume_total{wv} {}
};
float z; // z position of the layer
float height; // layer height
float depth; // depth of the layer based on all layers above
float extra_spacing;
float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
std::vector<ToolChange> tool_changes;
WipeTowerInfo(float z_par, float layer_height_par)
: z{z_par}, height{layer_height_par}, depth{0}, extra_spacing{1.f} {}
: z{z_par}, height{layer_height_par}, depth{0} {}
};
std::vector<WipeTowerInfo> m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
@@ -390,6 +387,7 @@ private:
WipeTowerWriter &writer,
const box_coordinates &cleaning_box,
const std::string& current_material,
const int old_temperature,
const int new_temperature);
void toolchange_Change(
@@ -412,4 +410,4 @@ private:
} // namespace Slic3r
#endif // WipeTowerQIDIMM_hpp_
#endif // slic3r_GCode_WipeTower_hpp_

View File

@@ -58,13 +58,14 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
|| will_go_down); // don't dig into the print
if (should_travel_to_tower) {
const Point xy_point = wipe_tower_point_to_object_point(gcodegen, start_pos);
gcode += gcodegen.m_label_objects.maybe_stop_instance();
gcode += gcodegen.retract_and_wipe();
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
const std::string comment{"Travel to a Wipe Tower"};
if (gcodegen.m_current_layer_first_position) {
if (gcodegen.last_position) {
gcode += gcodegen.travel_to(
*gcodegen.last_position, xy_point, ExtrusionRole::Mixed, comment
*gcodegen.last_position, xy_point, ExtrusionRole::Mixed, comment, [](){return "";}
);
} else {
gcode += gcodegen.writer().travel_to_xy(gcodegen.point_to_gcode(xy_point), comment);
@@ -72,7 +73,7 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
}
} else {
const Vec3crd point = to_3d(xy_point, scaled(z));
gcode += gcodegen.travel_to_first_position(point, current_z);
gcode += gcodegen.travel_to_first_position(point, current_z, ExtrusionRole::Mixed, [](){return "";});
}
gcode += gcodegen.unretract();
} else {
@@ -93,12 +94,10 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
gcodegen.m_wipe.reset_path(); // We don't want wiping on the ramming lines.
toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z); // TODO: toolchange_z vs print_z
if (gcodegen.config().wipe_tower) {
if (tcr.priming) {
const double return_to_z{tcr.print_z + gcodegen.config().z_offset.value};
deretraction_str += gcodegen.writer().get_travel_to_z_gcode(return_to_z, "set priming layer Z");
} else {
deretraction_str += gcodegen.writer().travel_to_z(z, "restore layer Z");
}
deretraction_str += gcodegen.writer().get_travel_to_z_gcode(z, "restore layer Z");
Vec3d position{gcodegen.writer().get_position()};
position.z() = z;
gcodegen.writer().update_position(position);
deretraction_str += gcodegen.unretract();
}
@@ -111,7 +110,10 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
boost::replace_first(tcr_rotated_gcode, "[deretraction_from_wipe_tower_generator]", deretraction_str);
std::string tcr_gcode;
unescape_string_cstyle(tcr_rotated_gcode, tcr_gcode);
if (gcodegen.config().default_acceleration > 0)
gcode += gcodegen.writer().set_print_acceleration(fast_round_up<unsigned int>(gcodegen.config().wipe_tower_acceleration.value));
gcode += tcr_gcode;
gcode += gcodegen.writer().set_print_acceleration(fast_round_up<unsigned int>(gcodegen.config().default_acceleration.value));
// A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy(end_pos.cast<double>());
@@ -136,7 +138,7 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
}
// Let the planner know we are traveling between objects.
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
return gcode;
}
@@ -262,10 +264,11 @@ std::string WipeTowerIntegration::tool_change(GCodeGenerator &gcodegen, int extr
std::string WipeTowerIntegration::finalize(GCodeGenerator &gcodegen)
{
std::string gcode;
if (std::abs(gcodegen.writer().get_position().z() - m_final_purge.print_z) > EPSILON)
const double purge_z{m_final_purge.print_z + gcodegen.config().z_offset.value};
if (std::abs(gcodegen.writer().get_position().z() - purge_z) > EPSILON)
gcode += gcodegen.generate_travel_gcode(
{{gcodegen.last_position->x(), gcodegen.last_position->y(), scaled(m_final_purge.print_z)}},
"move to safe place for purging"
{{gcodegen.last_position->x(), gcodegen.last_position->y(), scaled(purge_z)}},
"move to safe place for purging", [](){return "";}
);
gcode += append_tcr(gcodegen, m_final_purge, -1);
return gcode;