Merge branch 'lm_wipe_tower_xl'
This commit is contained in:
commit
3558006a0b
@ -776,7 +776,7 @@ public:
|
||||
static int nil_value() { return std::numeric_limits<int>::max(); }
|
||||
// A scalar is nil, or all values of a vector are nil.
|
||||
bool is_nil() const override { for (auto v : this->values) if (v != nil_value()) return false; return true; }
|
||||
bool is_nil(size_t idx) const override { return this->values[idx] == nil_value(); }
|
||||
bool is_nil(size_t idx) const override { return values[idx < this->values.size() ? idx : 0] == nil_value(); }
|
||||
|
||||
std::string serialize() const override
|
||||
{
|
||||
|
@ -113,26 +113,19 @@ namespace Slic3r {
|
||||
{
|
||||
std::string gcode;
|
||||
|
||||
// move to the nearest standby point
|
||||
if (!this->standby_points.empty()) {
|
||||
// get current position in print coordinates
|
||||
Vec3d writer_pos = gcodegen.writer().get_position();
|
||||
Point pos = Point::new_scale(writer_pos(0), writer_pos(1));
|
||||
|
||||
// find standby point
|
||||
Point standby_point = nearest_point(this->standby_points, pos).first;
|
||||
|
||||
/* We don't call gcodegen.travel_to() because we don't need retraction (it was already
|
||||
triggered by the caller) nor avoid_crossing_perimeters and also because the coordinates
|
||||
of the destination point must not be transformed by origin nor current extruder offset. */
|
||||
gcode += gcodegen.writer().travel_to_xy(unscale(standby_point),
|
||||
"move to standby position");
|
||||
}
|
||||
|
||||
if (gcodegen.config().standby_temperature_delta.value != 0) {
|
||||
// we assume that heating is always slower than cooling, so no need to block
|
||||
gcode += gcodegen.writer().set_temperature
|
||||
(this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false, gcodegen.writer().extruder()->id());
|
||||
unsigned int extruder_id = gcodegen.writer().extruder()->id();
|
||||
const ConfigOptionIntsNullable& filament_idle_temp = gcodegen.config().idle_temperature;
|
||||
if (filament_idle_temp.is_nil(extruder_id)) {
|
||||
// There is no idle temperature defined in filament settings.
|
||||
// Use the delta value from print config.
|
||||
if (gcodegen.config().standby_temperature_delta.value != 0) {
|
||||
// we assume that heating is always slower than cooling, so no need to block
|
||||
gcode += gcodegen.writer().set_temperature
|
||||
(this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false, extruder_id);
|
||||
}
|
||||
} else {
|
||||
// Use the value from filament settings. That one is absolute, not delta.
|
||||
gcode += gcodegen.writer().set_temperature(filament_idle_temp.get_at(extruder_id), false, extruder_id);
|
||||
}
|
||||
|
||||
return gcode;
|
||||
@ -145,8 +138,7 @@ namespace Slic3r {
|
||||
std::string();
|
||||
}
|
||||
|
||||
int
|
||||
OozePrevention::_get_temp(GCode& gcodegen)
|
||||
int OozePrevention::_get_temp(const GCode& gcodegen) const
|
||||
{
|
||||
return (gcodegen.layer() != nullptr && gcodegen.layer()->id() == 0)
|
||||
? gcodegen.config().first_layer_temperature.get_at(gcodegen.writer().extruder()->id())
|
||||
@ -238,8 +230,16 @@ namespace Slic3r {
|
||||
|
||||
std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation);
|
||||
|
||||
if (! tcr.priming) {
|
||||
// Move over the wipe tower.
|
||||
double current_z = gcodegen.writer().get_position().z();
|
||||
if (z == -1.) // in case no specific z was provided, print at current_z pos
|
||||
z = current_z;
|
||||
|
||||
const bool needs_toolchange = gcodegen.writer().need_toolchange(new_extruder_id);
|
||||
const bool will_go_down = ! is_approx(z, current_z);
|
||||
|
||||
if (! needs_toolchange || (gcodegen.config().single_extruder_multi_material && ! tcr.priming)) {
|
||||
// Move over the wipe tower. If this is not single-extruder MM, the first wipe tower move following the
|
||||
// toolchange will travel there anyway (if there is a toolchange).
|
||||
gcode += gcodegen.retract();
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
|
||||
gcode += gcodegen.travel_to(
|
||||
@ -248,82 +248,36 @@ namespace Slic3r {
|
||||
"Travel to a Wipe Tower");
|
||||
gcode += gcodegen.unretract();
|
||||
}
|
||||
|
||||
double current_z = gcodegen.writer().get_position().z();
|
||||
if (z == -1.) // in case no specific z was provided, print at current_z pos
|
||||
z = current_z;
|
||||
if (! is_approx(z, current_z)) {
|
||||
|
||||
if (will_go_down) {
|
||||
gcode += gcodegen.writer().retract();
|
||||
gcode += gcodegen.writer().travel_to_z(z, "Travel down to the last wipe tower layer.");
|
||||
gcode += gcodegen.writer().unretract();
|
||||
}
|
||||
|
||||
|
||||
// Process the end filament gcode.
|
||||
std::string end_filament_gcode_str;
|
||||
if (gcodegen.writer().extruder() != nullptr) {
|
||||
// Process the custom end_filament_gcode in case of single_extruder_multi_material.
|
||||
unsigned int old_extruder_id = gcodegen.writer().extruder()->id();
|
||||
const std::string& end_filament_gcode = gcodegen.config().end_filament_gcode.get_at(old_extruder_id);
|
||||
if (gcodegen.writer().extruder() != nullptr && !end_filament_gcode.empty()) {
|
||||
end_filament_gcode_str = gcodegen.placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id);
|
||||
check_add_eol(end_filament_gcode_str);
|
||||
}
|
||||
}
|
||||
|
||||
// Process the custom toolchange_gcode. If it is empty, provide a simple Tn command to change the filament.
|
||||
// Otherwise, leave control to the user completely.
|
||||
std::string toolchange_gcode_str;
|
||||
const std::string& toolchange_gcode = gcodegen.config().toolchange_gcode.value;
|
||||
if (! toolchange_gcode.empty()) {
|
||||
DynamicConfig config;
|
||||
int previous_extruder_id = gcodegen.writer().extruder() ? (int)gcodegen.writer().extruder()->id() : -1;
|
||||
config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id));
|
||||
config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id));
|
||||
config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index));
|
||||
config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z));
|
||||
config.set_key_value("toolchange_z", new ConfigOptionFloat(z));
|
||||
config.set_key_value("max_layer_z", new ConfigOptionFloat(gcodegen.m_max_layer_z));
|
||||
toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config);
|
||||
check_add_eol(toolchange_gcode_str);
|
||||
std::string deretraction_str;
|
||||
if (tcr.priming || (new_extruder_id >= 0 && needs_toolchange)) {
|
||||
if (gcodegen.config().single_extruder_multi_material)
|
||||
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)
|
||||
deretraction_str = gcodegen.unretract();
|
||||
}
|
||||
|
||||
std::string toolchange_command;
|
||||
if (tcr.priming || (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id)))
|
||||
toolchange_command = gcodegen.writer().toolchange(new_extruder_id);
|
||||
if (!custom_gcode_changes_tool(toolchange_gcode_str, gcodegen.writer().toolchange_prefix(), new_extruder_id))
|
||||
toolchange_gcode_str += toolchange_command;
|
||||
else {
|
||||
// We have informed the m_writer about the current extruder_id, we can ignore the generated G-code.
|
||||
}
|
||||
|
||||
gcodegen.placeholder_parser().set("current_extruder", new_extruder_id);
|
||||
|
||||
|
||||
// Process the start filament gcode.
|
||||
std::string start_filament_gcode_str;
|
||||
const std::string& start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(new_extruder_id);
|
||||
if (!start_filament_gcode.empty()) {
|
||||
// Process the start_filament_gcode for the active filament only.
|
||||
DynamicConfig config;
|
||||
config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index));
|
||||
config.set_key_value("layer_z", new ConfigOptionFloat(gcodegen.writer().get_position()(2) - gcodegen.m_config.z_offset.value));
|
||||
config.set_key_value("max_layer_z", new ConfigOptionFloat(gcodegen.m_max_layer_z));
|
||||
config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id));
|
||||
start_filament_gcode_str = gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config);
|
||||
check_add_eol(start_filament_gcode_str);
|
||||
}
|
||||
|
||||
// Insert the end filament, toolchange, and start filament gcode into the generated gcode.
|
||||
// Insert the toolchange and deretraction gcode into the generated gcode.
|
||||
DynamicConfig config;
|
||||
config.set_key_value("end_filament_gcode", new ConfigOptionString(end_filament_gcode_str));
|
||||
config.set_key_value("toolchange_gcode", new ConfigOptionString(toolchange_gcode_str));
|
||||
config.set_key_value("start_filament_gcode", new ConfigOptionString(start_filament_gcode_str));
|
||||
config.set_key_value("deretraction_from_wipe_tower_generator", new ConfigOptionString(deretraction_str));
|
||||
std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config);
|
||||
unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode);
|
||||
gcode += tcr_gcode;
|
||||
check_add_eol(toolchange_gcode_str);
|
||||
|
||||
|
||||
// A phony move to the end position at the wipe tower.
|
||||
gcodegen.writer().travel_to_xy(end_pos.cast<double>());
|
||||
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos));
|
||||
@ -898,34 +852,7 @@ namespace DoExport {
|
||||
|
||||
static void init_ooze_prevention(const Print &print, OozePrevention &ooze_prevention)
|
||||
{
|
||||
// Calculate wiping points if needed
|
||||
if (print.config().ooze_prevention.value && ! print.config().single_extruder_multi_material) {
|
||||
Points skirt_points;
|
||||
for (const ExtrusionEntity *ee : print.skirt().entities)
|
||||
for (const ExtrusionPath &path : dynamic_cast<const ExtrusionLoop*>(ee)->paths)
|
||||
append(skirt_points, path.polyline.points);
|
||||
if (! skirt_points.empty()) {
|
||||
Polygon outer_skirt = Slic3r::Geometry::convex_hull(skirt_points);
|
||||
Polygons skirts;
|
||||
for (unsigned int extruder_id : print.extruders()) {
|
||||
const Vec2d &extruder_offset = print.config().extruder_offset.get_at(extruder_id);
|
||||
Polygon s(outer_skirt);
|
||||
s.translate(Point::new_scale(-extruder_offset(0), -extruder_offset(1)));
|
||||
skirts.emplace_back(std::move(s));
|
||||
}
|
||||
ooze_prevention.enable = true;
|
||||
ooze_prevention.standby_points = offset(Slic3r::Geometry::convex_hull(skirts), float(scale_(3.))).front().equally_spaced_points(float(scale_(10.)));
|
||||
#if 0
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output(
|
||||
"ooze_prevention.svg",
|
||||
red_polygons => \@skirts,
|
||||
polygons => [$outer_skirt],
|
||||
points => $gcodegen->ooze_prevention->standby_points,
|
||||
);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
ooze_prevention.enable = print.config().ooze_prevention.value && ! print.config().single_extruder_multi_material;
|
||||
}
|
||||
|
||||
// Fill in print_statistics and return formatted string containing filament statistics to be inserted into G-code comment section.
|
||||
@ -1263,6 +1190,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() }));
|
||||
m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() }));
|
||||
m_placeholder_parser.set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
|
||||
|
||||
std::vector<unsigned char> is_extruder_used(print.config().nozzle_diameter.size(), 0);
|
||||
for (unsigned int extruder_id : print.extruders())
|
||||
is_extruder_used[extruder_id] = true;
|
||||
m_placeholder_parser.set("is_extruder_used", new ConfigOptionBools(is_extruder_used));
|
||||
}
|
||||
std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, initial_extruder_id);
|
||||
// Set bed temperature if the start G-code does not contain any bed temp control G-codes.
|
||||
@ -1282,8 +1214,9 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
// Set other general things.
|
||||
file.write(this->preamble());
|
||||
|
||||
// Calculate wiping points if needed
|
||||
// Enable ooze prevention if configured so.
|
||||
DoExport::init_ooze_prevention(print, m_ooze_prevention);
|
||||
|
||||
print.throw_if_canceled();
|
||||
|
||||
// Collect custom seam data from all objects.
|
||||
@ -1814,7 +1747,7 @@ void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Pr
|
||||
m_writer.set_temperature(temp, wait, first_printing_extruder_id);
|
||||
} else {
|
||||
// Custom G-code does not set the extruder temperature. Do it now.
|
||||
if (print.config().single_extruder_multi_material.value) {
|
||||
if (print.config().single_extruder_multi_material.value || m_ooze_prevention.enable) {
|
||||
// Set temperature of the first printing extruder only.
|
||||
int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id);
|
||||
if (temp > 0)
|
||||
@ -2136,11 +2069,14 @@ LayerResult GCode::process_layer(
|
||||
// Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent
|
||||
// first_layer_temperature vs. temperature settings.
|
||||
for (const Extruder &extruder : m_writer.extruders()) {
|
||||
if (print.config().single_extruder_multi_material.value && extruder.id() != m_writer.extruder()->id())
|
||||
if (print.config().single_extruder_multi_material.value || m_ooze_prevention.enable) {
|
||||
// In single extruder multi material mode, set the temperature for the current extruder only.
|
||||
continue;
|
||||
// The same applies when ooze prevention is enabled.
|
||||
if (extruder.id() != m_writer.extruder()->id())
|
||||
continue;
|
||||
}
|
||||
int temperature = print.config().temperature.get_at(extruder.id());
|
||||
if (temperature > 0 && temperature != print.config().first_layer_temperature.get_at(extruder.id()))
|
||||
if (temperature > 0 && (temperature != print.config().first_layer_temperature.get_at(extruder.id())))
|
||||
gcode += m_writer.set_temperature(temperature, false, extruder.id());
|
||||
}
|
||||
gcode += m_writer.set_bed_temperature(print.config().bed_temperature.get_at(first_extruder_id));
|
||||
@ -3186,6 +3122,9 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
|
||||
if (! start_filament_gcode.empty()) {
|
||||
// Process the start_filament_gcode for the filament.
|
||||
DynamicConfig config;
|
||||
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
|
||||
config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position()(2) - m_config.z_offset.value));
|
||||
config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
|
||||
config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id)));
|
||||
gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config);
|
||||
check_add_eol(gcode);
|
||||
@ -3201,8 +3140,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
|
||||
m_wipe.reset_path();
|
||||
|
||||
if (m_writer.extruder() != nullptr) {
|
||||
// Process the custom end_filament_gcode. set_extruder() is only called if there is no wipe tower
|
||||
// so it should not be injected twice.
|
||||
// Process the custom end_filament_gcode.
|
||||
unsigned int old_extruder_id = m_writer.extruder()->id();
|
||||
const std::string &end_filament_gcode = m_config.end_filament_gcode.get_at(old_extruder_id);
|
||||
if (! end_filament_gcode.empty()) {
|
||||
@ -3212,8 +3150,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
|
||||
}
|
||||
|
||||
|
||||
// If ooze prevention is enabled, park current extruder in the nearest
|
||||
// standby point and set it to the standby temperature.
|
||||
// If ooze prevention is enabled, set current extruder to the standby temperature.
|
||||
if (m_ooze_prevention.enable && m_writer.extruder() != nullptr)
|
||||
gcode += m_ooze_prevention.pre_toolchange(*this);
|
||||
|
||||
@ -3257,6 +3194,9 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
|
||||
if (! start_filament_gcode.empty()) {
|
||||
// Process the start_filament_gcode for the new filament.
|
||||
DynamicConfig config;
|
||||
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
|
||||
config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position()(2) - m_config.z_offset.value));
|
||||
config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
|
||||
config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id)));
|
||||
gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config);
|
||||
check_add_eol(gcode);
|
||||
|
@ -39,14 +39,13 @@ struct PrintInstance;
|
||||
class OozePrevention {
|
||||
public:
|
||||
bool enable;
|
||||
Points standby_points;
|
||||
|
||||
OozePrevention() : enable(false) {}
|
||||
std::string pre_toolchange(GCode &gcodegen);
|
||||
std::string post_toolchange(GCode &gcodegen);
|
||||
|
||||
private:
|
||||
int _get_temp(GCode &gcodegen);
|
||||
int _get_temp(const GCode &gcodegen) const;
|
||||
};
|
||||
|
||||
class Wipe {
|
||||
|
@ -71,6 +71,8 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
WipeTowerWriter& set_position(const Vec2f &pos) { m_current_pos = pos; return *this; }
|
||||
|
||||
WipeTowerWriter& set_initial_tool(size_t tool) { m_current_tool = tool; return *this; }
|
||||
|
||||
WipeTowerWriter& set_z(float z)
|
||||
@ -802,22 +804,26 @@ void WipeTower::toolchange_Unload(
|
||||
{
|
||||
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 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 Vec2f ramming_start_pos = Vec2f(xl, cleaning_box.ld.y() + m_depth_traversed + y_step/2.f);
|
||||
|
||||
writer.append("; CP TOOLCHANGE UNLOAD\n")
|
||||
.change_analyzer_line_width(line_width);
|
||||
|
||||
unsigned i = 0; // iterates through ramming_speed
|
||||
m_left_to_right = true; // current direction of ramming
|
||||
float remaining = xr - xl ; // keeps track of distance to the next turnaround
|
||||
float e_done = 0; // measures E move done from each segment
|
||||
|
||||
writer.travel(xl, cleaning_box.ld.y() + m_depth_traversed + y_step/2.f ); // move to starting position
|
||||
float e_done = 0; // measures E move done from each segment
|
||||
|
||||
if (m_semm)
|
||||
writer.travel(ramming_start_pos); // move to starting position
|
||||
else
|
||||
writer.set_position(ramming_start_pos);
|
||||
// if the ending point of the ram would end up in mid air, align it with the end of the wipe tower:
|
||||
if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion )) {
|
||||
if (m_semm && (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion ))) {
|
||||
|
||||
// this is y of the center of previous sparse infill border
|
||||
float sparse_beginning_y = 0.f;
|
||||
@ -849,7 +855,7 @@ void WipeTower::toolchange_Unload(
|
||||
writer.disable_linear_advance();
|
||||
|
||||
// now the ramming itself:
|
||||
while (i < m_filpar[m_current_tool].ramming_speed.size())
|
||||
while (m_semm && i < m_filpar[m_current_tool].ramming_speed.size())
|
||||
{
|
||||
const float x = volume_to_length(m_filpar[m_current_tool].ramming_speed[i] * 0.25f, line_width, m_layer_height);
|
||||
const float e = m_filpar[m_current_tool].ramming_speed[i] * 0.25f / filament_area(); // transform volume per sec to E move;
|
||||
@ -898,7 +904,7 @@ void WipeTower::toolchange_Unload(
|
||||
|
||||
// Cooling:
|
||||
const int& number_of_moves = m_filpar[m_current_tool].cooling_moves;
|
||||
if (number_of_moves > 0) {
|
||||
if (m_semm && number_of_moves > 0) {
|
||||
const float& initial_speed = m_filpar[m_current_tool].cooling_initial_speed;
|
||||
const float& final_speed = m_filpar[m_current_tool].cooling_final_speed;
|
||||
|
||||
@ -916,14 +922,20 @@ void WipeTower::toolchange_Unload(
|
||||
}
|
||||
}
|
||||
|
||||
// let's wait is necessary:
|
||||
writer.wait(m_filpar[m_current_tool].delay);
|
||||
// we should be at the beginning of the cooling tube again - let's move to parking position:
|
||||
writer.retract(-m_cooling_tube_length/2.f+m_parking_pos_retraction-m_cooling_tube_retraction, 2000);
|
||||
if (m_semm) {
|
||||
// let's wait is necessary:
|
||||
writer.wait(m_filpar[m_current_tool].delay);
|
||||
// we should be at the beginning of the cooling tube again - let's move to parking position:
|
||||
writer.retract(-m_cooling_tube_length/2.f+m_parking_pos_retraction-m_cooling_tube_retraction, 2000);
|
||||
}
|
||||
|
||||
// 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
|
||||
writer.travel(end_of_ramming.x(), end_of_ramming.y() + (y_step/m_extra_spacing-m_perimeter_width) / 2.f + m_perimeter_width, 2400.f);
|
||||
Vec2f pos = Vec2f(end_of_ramming.x(), end_of_ramming.y() + (y_step/m_extra_spacing-m_perimeter_width) / 2.f + m_perimeter_width);
|
||||
if (m_semm)
|
||||
writer.travel(pos, 2400.f);
|
||||
else
|
||||
writer.set_position(pos);
|
||||
|
||||
writer.resume_preview()
|
||||
.flush_planner_queue();
|
||||
@ -941,7 +953,7 @@ void WipeTower::toolchange_Change(
|
||||
|
||||
// This is where we want to place the custom gcodes. We will use placeholders for this.
|
||||
// These will be substituted by the actual gcodes when the gcode is generated.
|
||||
writer.append("[end_filament_gcode]\n");
|
||||
//writer.append("[end_filament_gcode]\n");
|
||||
writer.append("[toolchange_gcode]\n");
|
||||
|
||||
// Travel to where we assume we are. Custom toolchange or some special T code handling (parking extruder etc)
|
||||
@ -952,11 +964,12 @@ void WipeTower::toolchange_Change(
|
||||
.append(std::string("G1 X") + Slic3r::float_to_string_decimal_point(current_pos.x())
|
||||
+ " Y" + Slic3r::float_to_string_decimal_point(current_pos.y())
|
||||
+ never_skip_tag() + "\n");
|
||||
writer.append("[deretraction_from_wipe_tower_generator]");
|
||||
|
||||
// The toolchange Tn command will be inserted later, only in case that the user does
|
||||
// not provide a custom toolchange gcode.
|
||||
writer.set_tool(new_tool); // This outputs nothing, the writer just needs to know the tool has changed.
|
||||
writer.append("[start_filament_gcode]\n");
|
||||
//writer.append("[start_filament_gcode]\n");
|
||||
|
||||
writer.flush_planner_queue();
|
||||
m_current_tool = new_tool;
|
||||
|
@ -290,7 +290,6 @@ private:
|
||||
// Extruder specific parameters.
|
||||
std::vector<FilamentParameters> m_filpar;
|
||||
|
||||
|
||||
// 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.
|
||||
|
@ -470,7 +470,7 @@ static std::vector<std::string> s_Preset_filament_options {
|
||||
"extrusion_multiplier", "filament_density", "filament_cost", "filament_spool_weight", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time",
|
||||
"filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves",
|
||||
"filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower",
|
||||
"temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed",
|
||||
"temperature", "idle_temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed",
|
||||
"max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "full_fan_speed_layer", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed",
|
||||
"start_filament_gcode", "end_filament_gcode",
|
||||
// Retract overrides
|
||||
|
@ -191,6 +191,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
|| opt_key == "infill_first"
|
||||
|| opt_key == "single_extruder_multi_material"
|
||||
|| opt_key == "temperature"
|
||||
|| opt_key == "idle_temperature"
|
||||
|| opt_key == "wipe_tower"
|
||||
|| opt_key == "wipe_tower_width"
|
||||
|| opt_key == "wipe_tower_brim_width"
|
||||
@ -347,7 +348,7 @@ std::vector<ObjectID> Print::print_object_ids() const
|
||||
|
||||
bool Print::has_infinite_skirt() const
|
||||
{
|
||||
return (m_config.draft_shield == dsEnabled && m_config.skirts > 0) || (m_config.ooze_prevention && this->extruders().size() > 1);
|
||||
return (m_config.draft_shield == dsEnabled && m_config.skirts > 0)/* || (m_config.ooze_prevention && this->extruders().size() > 1)*/;
|
||||
}
|
||||
|
||||
bool Print::has_skirt() const
|
||||
@ -505,8 +506,8 @@ std::string Print::validate(std::string* warning) const
|
||||
return L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter, RepRapFirmware and Repetier G-code flavors.");
|
||||
if (! m_config.use_relative_e_distances)
|
||||
return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).");
|
||||
if (m_config.ooze_prevention)
|
||||
return L("Ooze prevention is currently not supported with the wipe tower enabled.");
|
||||
if (m_config.ooze_prevention && m_config.single_extruder_multi_material)
|
||||
return L("Ooze prevention is only supported with the wipe tower when 'single_extruder_multi_material' is off.");
|
||||
if (m_config.use_volumetric_e)
|
||||
return L("The Wipe Tower currently does not support volumetric E (use_volumetric_e=0).");
|
||||
if (m_config.complete_objects && extruders.size() > 1)
|
||||
|
@ -229,6 +229,11 @@ static void assign_printer_technology_to_unknown(t_optiondef_map &options, Print
|
||||
kvp.second.printer_technology = printer_technology;
|
||||
}
|
||||
|
||||
// Maximum extruder temperature, bumped to 1500 to support printing of glass.
|
||||
namespace {
|
||||
const int max_temp = 1500;
|
||||
};
|
||||
|
||||
PrintConfigDef::PrintConfigDef()
|
||||
{
|
||||
this->init_common_params();
|
||||
@ -1972,9 +1977,7 @@ void PrintConfigDef::init_fff_params()
|
||||
|
||||
def = this->add("ooze_prevention", coBool);
|
||||
def->label = L("Enable");
|
||||
def->tooltip = L("This option will drop the temperature of the inactive extruders to prevent oozing. "
|
||||
"It will enable a tall skirt automatically and move extruders outside such "
|
||||
"skirt when changing temperatures.");
|
||||
def->tooltip = L("This option will drop the temperature of the inactive extruders to prevent oozing. ");
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
@ -2475,7 +2478,8 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("standby_temperature_delta", coInt);
|
||||
def->label = L("Temperature variation");
|
||||
def->tooltip = L("Temperature difference to be applied when an extruder is not active. "
|
||||
"Enables a full-height \"sacrificial\" skirt on which the nozzles are periodically wiped.");
|
||||
"The value is not used when 'idle_temperature' in filament settings "
|
||||
"is defined.");
|
||||
def->sidetext = "∆°C";
|
||||
def->min = -max_temp;
|
||||
def->max = max_temp;
|
||||
@ -3731,6 +3735,15 @@ void PrintConfigDef::init_sla_params()
|
||||
def->min = 0;
|
||||
def->set_default_value(new ConfigOptionFloat(0.3));
|
||||
|
||||
def = this->add_nullable("idle_temperature", coInts);
|
||||
def->label = L("Idle temperature");
|
||||
def->tooltip = L("Nozzle temperature when the tool is currently not used in multi-tool setups."
|
||||
"This is only used when 'Ooze prevention is active in Print Settings.'");
|
||||
def->sidetext = L("°C");
|
||||
def->min = 0;
|
||||
def->max = max_temp;
|
||||
def->set_default_value(new ConfigOptionIntsNullable { ConfigOptionIntsNullable::nil_value() });
|
||||
|
||||
def = this->add("bottle_volume", coFloat);
|
||||
def->label = L("Bottle volume");
|
||||
def->tooltip = L("Bottle volume");
|
||||
@ -4511,6 +4524,12 @@ std::string validate(const FullPrintConfig &cfg)
|
||||
assert(opt != nullptr);
|
||||
const ConfigOptionDef *optdef = print_config_def.get(opt_key);
|
||||
assert(optdef != nullptr);
|
||||
|
||||
if (opt->nullable() && opt->is_nil()) {
|
||||
// Do not check nil values
|
||||
continue;
|
||||
}
|
||||
|
||||
bool out_of_range = false;
|
||||
switch (opt->type()) {
|
||||
case coFloat:
|
||||
|
@ -769,6 +769,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
((ConfigOptionFloatOrPercent, first_layer_height))
|
||||
((ConfigOptionFloatOrPercent, first_layer_speed))
|
||||
((ConfigOptionInts, first_layer_temperature))
|
||||
((ConfigOptionIntsNullable, idle_temperature))
|
||||
((ConfigOptionInts, full_fan_speed_layer))
|
||||
((ConfigOptionFloat, infill_acceleration))
|
||||
((ConfigOptionBool, infill_first))
|
||||
|
@ -762,28 +762,26 @@ void SpinCtrl::BUILD() {
|
||||
if (m_opt.width >= 0) size.SetWidth(m_opt.width*m_em_unit);
|
||||
|
||||
wxString text_value = wxString("");
|
||||
int default_value = 0;
|
||||
int default_value = UNDEF_VALUE;
|
||||
|
||||
switch (m_opt.type) {
|
||||
case coInt:
|
||||
default_value = m_opt.default_value->getInt();
|
||||
text_value = wxString::Format(_T("%i"), default_value);
|
||||
break;
|
||||
case coInts:
|
||||
{
|
||||
const ConfigOptionInts *vec = m_opt.get_default_value<ConfigOptionInts>();
|
||||
if (vec == nullptr || vec->empty()) break;
|
||||
for (size_t id = 0; id < vec->size(); ++id)
|
||||
{
|
||||
default_value = vec->get_at(id);
|
||||
text_value += wxString::Format(_T("%i"), default_value);
|
||||
}
|
||||
default_value = m_opt.get_default_value<ConfigOptionInts>()->get_at(m_opt_idx);
|
||||
if (m_opt.nullable)
|
||||
m_last_meaningful_value = default_value == ConfigOptionIntsNullable::nil_value() ? static_cast<int>(m_opt.max) : default_value;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (default_value != UNDEF_VALUE)
|
||||
text_value = wxString::Format(_T("%i"), default_value);
|
||||
|
||||
const int min_val = m_opt.min == -FLT_MAX
|
||||
#ifdef __WXOSX__
|
||||
// We will forcibly set the input value for SpinControl, since the value
|
||||
@ -882,6 +880,50 @@ void SpinCtrl::BUILD() {
|
||||
window = dynamic_cast<wxWindow*>(temp);
|
||||
}
|
||||
|
||||
void SpinCtrl::set_value(const boost::any& value, bool change_event/* = false*/)
|
||||
{
|
||||
m_disable_change_event = !change_event;
|
||||
tmp_value = boost::any_cast<int>(value);
|
||||
m_value = value;
|
||||
if (m_opt.nullable) {
|
||||
const bool m_is_na_val = tmp_value == ConfigOptionIntsNullable::nil_value();
|
||||
if (m_is_na_val)
|
||||
dynamic_cast<wxSpinCtrl*>(window)->SetValue(na_value());
|
||||
else {
|
||||
m_last_meaningful_value = value;
|
||||
dynamic_cast<wxSpinCtrl*>(window)->SetValue(tmp_value);
|
||||
}
|
||||
}
|
||||
else
|
||||
dynamic_cast<wxSpinCtrl*>(window)->SetValue(tmp_value);
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
|
||||
void SpinCtrl::set_last_meaningful_value()
|
||||
{
|
||||
const int val = boost::any_cast<int>(m_last_meaningful_value);
|
||||
dynamic_cast<wxSpinCtrl*>(window)->SetValue(val);
|
||||
tmp_value = val;
|
||||
propagate_value();
|
||||
}
|
||||
|
||||
void SpinCtrl::set_na_value()
|
||||
{
|
||||
dynamic_cast<wxSpinCtrl*>(window)->SetValue(na_value());
|
||||
m_value = ConfigOptionIntsNullable::nil_value();
|
||||
propagate_value();
|
||||
}
|
||||
|
||||
boost::any& SpinCtrl::get_value()
|
||||
{
|
||||
wxSpinCtrl* spin = static_cast<wxSpinCtrl*>(window);
|
||||
if (spin->GetTextValue() == na_value())
|
||||
return m_value;
|
||||
|
||||
int value = spin->GetValue();
|
||||
return m_value = value;
|
||||
}
|
||||
|
||||
void SpinCtrl::propagate_value()
|
||||
{
|
||||
// check if value was really changed
|
||||
|
@ -330,24 +330,18 @@ public:
|
||||
void BUILD() override;
|
||||
/// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
|
||||
void propagate_value() ;
|
||||
|
||||
/*
|
||||
void set_value(const std::string& value, bool change_event = false) {
|
||||
m_disable_change_event = !change_event;
|
||||
dynamic_cast<wxSpinCtrl*>(window)->SetValue(value);
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
void set_value(const boost::any& value, bool change_event = false) override {
|
||||
m_disable_change_event = !change_event;
|
||||
tmp_value = boost::any_cast<int>(value);
|
||||
m_value = value;
|
||||
dynamic_cast<wxSpinCtrl*>(window)->SetValue(tmp_value);
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
*/
|
||||
void set_value(const boost::any& value, bool change_event = false) override;
|
||||
void set_last_meaningful_value() override;
|
||||
void set_na_value() override;
|
||||
|
||||
boost::any& get_value() override {
|
||||
int value = static_cast<wxSpinCtrl*>(window)->GetValue();
|
||||
return m_value = value;
|
||||
}
|
||||
boost::any& get_value() override;
|
||||
|
||||
void msw_rescale() override;
|
||||
|
||||
|
@ -1826,45 +1826,59 @@ static void validate_custom_gcode_cb(Tab* tab, const wxString& title, const t_co
|
||||
tab->on_value_change(opt_key, value);
|
||||
}
|
||||
|
||||
void TabFilament::create_line_with_near_label_widget(ConfigOptionsGroupShp optgroup, const std::string& opt_key, int opt_index/* = 0*/)
|
||||
{
|
||||
Line line {"",""};
|
||||
if (opt_key == "filament_retract_lift_above" || opt_key == "filament_retract_lift_below") {
|
||||
Option opt = optgroup->get_option(opt_key);
|
||||
opt.opt.label = opt.opt.full_label;
|
||||
line = optgroup->create_single_option_line(opt);
|
||||
}
|
||||
else
|
||||
line = optgroup->create_single_option_line(optgroup->get_option(opt_key));
|
||||
|
||||
line.near_label_widget = [this, optgroup_wk = ConfigOptionsGroupWkp(optgroup), opt_key, opt_index](wxWindow* parent) {
|
||||
wxCheckBox* check_box = new wxCheckBox(parent, wxID_ANY, "");
|
||||
|
||||
check_box->Bind(wxEVT_CHECKBOX, [optgroup_wk, opt_key, opt_index](wxCommandEvent& evt) {
|
||||
const bool is_checked = evt.IsChecked();
|
||||
if (auto optgroup_sh = optgroup_wk.lock(); optgroup_sh) {
|
||||
if (Field *field = optgroup_sh->get_fieldc(opt_key, opt_index); field != nullptr) {
|
||||
field->toggle(is_checked);
|
||||
if (is_checked)
|
||||
field->set_last_meaningful_value();
|
||||
else
|
||||
field->set_na_value();
|
||||
}
|
||||
}
|
||||
}, check_box->GetId());
|
||||
|
||||
m_overrides_options[opt_key] = check_box;
|
||||
return check_box;
|
||||
};
|
||||
|
||||
optgroup->append_line(line);
|
||||
}
|
||||
|
||||
void TabFilament::update_line_with_near_label_widget(ConfigOptionsGroupShp optgroup, const std::string& opt_key, int opt_index/* = 0*/, bool is_checked/* = true*/)
|
||||
{
|
||||
if (!m_overrides_options[opt_key])
|
||||
return;
|
||||
m_overrides_options[opt_key]->Enable(is_checked);
|
||||
|
||||
is_checked &= !m_config->option(opt_key)->is_nil();
|
||||
m_overrides_options[opt_key]->SetValue(is_checked);
|
||||
|
||||
Field* field = optgroup->get_fieldc(opt_key, opt_index);
|
||||
if (field != nullptr)
|
||||
field->toggle(is_checked);
|
||||
}
|
||||
|
||||
void TabFilament::add_filament_overrides_page()
|
||||
{
|
||||
PageShp page = add_options_page(L("Filament Overrides"), "wrench");
|
||||
ConfigOptionsGroupShp optgroup = page->new_optgroup(L("Retraction"));
|
||||
|
||||
auto append_single_option_line = [optgroup, this](const std::string& opt_key, int opt_index)
|
||||
{
|
||||
Line line {"",""};
|
||||
if (opt_key == "filament_retract_lift_above" || opt_key == "filament_retract_lift_below") {
|
||||
Option opt = optgroup->get_option(opt_key);
|
||||
opt.opt.label = opt.opt.full_label;
|
||||
line = optgroup->create_single_option_line(opt);
|
||||
}
|
||||
else
|
||||
line = optgroup->create_single_option_line(optgroup->get_option(opt_key));
|
||||
|
||||
line.near_label_widget = [this, optgroup_wk = ConfigOptionsGroupWkp(optgroup), opt_key, opt_index](wxWindow* parent) {
|
||||
wxCheckBox* check_box = new wxCheckBox(parent, wxID_ANY, "");
|
||||
|
||||
check_box->Bind(wxEVT_CHECKBOX, [optgroup_wk, opt_key, opt_index](wxCommandEvent& evt) {
|
||||
const bool is_checked = evt.IsChecked();
|
||||
if (auto optgroup_sh = optgroup_wk.lock(); optgroup_sh) {
|
||||
if (Field *field = optgroup_sh->get_fieldc(opt_key, opt_index); field != nullptr) {
|
||||
field->toggle(is_checked);
|
||||
if (is_checked)
|
||||
field->set_last_meaningful_value();
|
||||
else
|
||||
field->set_na_value();
|
||||
}
|
||||
}
|
||||
}, check_box->GetId());
|
||||
|
||||
m_overrides_options[opt_key] = check_box;
|
||||
return check_box;
|
||||
};
|
||||
|
||||
optgroup->append_line(line);
|
||||
};
|
||||
|
||||
const int extruder_idx = 0; // #ys_FIXME
|
||||
|
||||
for (const std::string opt_key : { "filament_retract_length",
|
||||
@ -1879,7 +1893,7 @@ void TabFilament::add_filament_overrides_page()
|
||||
"filament_wipe",
|
||||
"filament_retract_before_wipe"
|
||||
})
|
||||
append_single_option_line(opt_key, extruder_idx);
|
||||
create_line_with_near_label_widget(optgroup, opt_key, extruder_idx);
|
||||
}
|
||||
|
||||
void TabFilament::update_filament_overrides_page()
|
||||
@ -1914,14 +1928,7 @@ void TabFilament::update_filament_overrides_page()
|
||||
for (const std::string& opt_key : opt_keys)
|
||||
{
|
||||
bool is_checked = opt_key=="filament_retract_length" ? true : have_retract_length;
|
||||
m_overrides_options[opt_key]->Enable(is_checked);
|
||||
|
||||
is_checked &= !m_config->option(opt_key)->is_nil();
|
||||
m_overrides_options[opt_key]->SetValue(is_checked);
|
||||
|
||||
Field* field = optgroup->get_fieldc(opt_key, extruder_idx);
|
||||
if (field != nullptr)
|
||||
field->toggle(is_checked);
|
||||
update_line_with_near_label_widget(optgroup, opt_key, extruder_idx, is_checked);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1952,6 +1959,9 @@ void TabFilament::build()
|
||||
};
|
||||
|
||||
optgroup = page->new_optgroup(L("Temperature"));
|
||||
|
||||
create_line_with_near_label_widget(optgroup, "idle_temperature");
|
||||
|
||||
Line line = { L("Nozzle"), "" };
|
||||
line.append_option(optgroup->get_option("first_layer_temperature"));
|
||||
line.append_option(optgroup->get_option("temperature"));
|
||||
@ -2142,6 +2152,14 @@ void TabFilament::toggle_options()
|
||||
|
||||
if (m_active_page->title() == "Filament Overrides")
|
||||
update_filament_overrides_page();
|
||||
|
||||
if (m_active_page->title() == "Filament") {
|
||||
Page* page = m_active_page;
|
||||
|
||||
const auto og_it = std::find_if(page->m_optgroups.begin(), page->m_optgroups.end(), [](const ConfigOptionsGroupShp og) { return og->title == "Temperature"; });
|
||||
if (og_it != page->m_optgroups.end())
|
||||
update_line_with_near_label_widget(*og_it, "idle_temperature");
|
||||
}
|
||||
}
|
||||
|
||||
void TabFilament::update()
|
||||
|
@ -436,6 +436,8 @@ private:
|
||||
ogStaticText* m_volumetric_speed_description_line {nullptr};
|
||||
ogStaticText* m_cooling_description_line {nullptr};
|
||||
|
||||
void create_line_with_near_label_widget(ConfigOptionsGroupShp optgroup, const std::string &opt_key, int opt_index = 0);
|
||||
void update_line_with_near_label_widget(ConfigOptionsGroupShp optgroup, const std::string &opt_key, int opt_index = 0, bool is_checked = true);
|
||||
void add_filament_overrides_page();
|
||||
void update_filament_overrides_page();
|
||||
void update_volumetric_flow_preset_hints();
|
||||
|
@ -108,18 +108,18 @@ SCENARIO("Ooze prevention", "[Multi]")
|
||||
|
||||
Polygon convex_hull = Geometry::convex_hull(extrusion_points);
|
||||
|
||||
THEN("all nozzles are outside skirt at toolchange") {
|
||||
Points t;
|
||||
sort_remove_duplicates(toolchange_points);
|
||||
size_t inside = 0;
|
||||
for (const auto &point : toolchange_points)
|
||||
for (const Vec2d &offset : print_config.extruder_offset.values) {
|
||||
Point p = point + scaled<coord_t>(offset);
|
||||
if (convex_hull.contains(p))
|
||||
++ inside;
|
||||
}
|
||||
REQUIRE(inside == 0);
|
||||
}
|
||||
// THEN("all nozzles are outside skirt at toolchange") {
|
||||
// Points t;
|
||||
// sort_remove_duplicates(toolchange_points);
|
||||
// size_t inside = 0;
|
||||
// for (const auto &point : toolchange_points)
|
||||
// for (const Vec2d &offset : print_config.extruder_offset.values) {
|
||||
// Point p = point + scaled<coord_t>(offset);
|
||||
// if (convex_hull.contains(p))
|
||||
// ++ inside;
|
||||
// }
|
||||
// REQUIRE(inside == 0);
|
||||
// }
|
||||
|
||||
#if 0
|
||||
require "Slic3r/SVG.pm";
|
||||
|
Loading…
Reference in New Issue
Block a user