Merge branch 'ms_cancel_object' into master_27x

This commit is contained in:
Lukas Matena 2024-03-14 15:43:18 +01:00
commit ea65285999
8 changed files with 364 additions and 51 deletions

View File

@ -576,7 +576,6 @@ GCodeGenerator::GCodeGenerator(const Print* print) :
m_brim_done(false), m_brim_done(false),
m_second_layer_things_done(false), m_second_layer_things_done(false),
m_silent_time_estimator_enabled(false), m_silent_time_estimator_enabled(false),
m_current_instance({nullptr, -1}),
m_print(print) m_print(print)
{} {}
@ -1248,7 +1247,10 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer
m_avoid_crossing_perimeters.use_external_mp_once(); m_avoid_crossing_perimeters.use_external_mp_once();
file.write(this->retract_and_wipe()); file.write(this->retract_and_wipe());
file.write(this->travel_to(*this->last_position, Point(0, 0), ExtrusionRole::None, "move to origin position for next object")); file.write(m_label_objects.maybe_stop_instance());
const double last_z{this->writer().get_position().z()};
file.write(this->writer().get_travel_to_z_gcode(last_z, "ensure z position"));
file.write(this->travel_to(*this->last_position, Point(0, 0), ExtrusionRole::None, "move to origin position for next object", [](){return "";}));
m_enable_cooling_markers = true; m_enable_cooling_markers = true;
// Disable motion planner when traveling to first object point. // Disable motion planner when traveling to first object point.
m_avoid_crossing_perimeters.disable_once(); m_avoid_crossing_perimeters.disable_once();
@ -1277,6 +1279,8 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
m_second_layer_things_done = false; m_second_layer_things_done = false;
prev_object = &object; prev_object = &object;
} }
file.write(m_label_objects.maybe_stop_instance());
} else { } else {
// Sort layers by Z. // Sort layers by Z.
// All extrusion moves with the same top layer height are extruded uninterrupted. // All extrusion moves with the same top layer height are extruded uninterrupted.
@ -1333,6 +1337,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
// and export G-code into file. // and export G-code into file.
this->process_layers(print, tool_ordering, print_object_instances_ordering, layers_to_print, this->process_layers(print, tool_ordering, print_object_instances_ordering, layers_to_print,
smooth_path_cache_global, file); smooth_path_cache_global, file);
file.write(m_label_objects.maybe_stop_instance());
if (m_wipe_tower) if (m_wipe_tower)
// Purge the extruder, pull out the active filament. // Purge the extruder, pull out the active filament.
file.write(m_wipe_tower->finalize(*this)); file.write(m_wipe_tower->finalize(*this));
@ -2206,6 +2211,11 @@ LayerResult GCodeGenerator::process_layer(
bool first_layer = layer.id() == 0; bool first_layer = layer.id() == 0;
unsigned int first_extruder_id = layer_tools.extruders.front(); unsigned int first_extruder_id = layer_tools.extruders.front();
const std::vector<InstanceToPrint> instances_to_print{sort_print_object_instances(layers, ordering, single_object_instance_idx)};
const PrintInstance* first_instance{instances_to_print.empty() ? nullptr : &instances_to_print.front().print_object.instances()[instances_to_print.front().instance_id]};
m_label_objects.update(first_instance);
// Initialize config with the 1st object to be printed at this layer. // Initialize config with the 1st object to be printed at this layer.
m_config.apply(layer.object()->config(), true); m_config.apply(layer.object()->config(), true);
@ -2352,6 +2362,11 @@ LayerResult GCodeGenerator::process_layer(
} }
if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) { if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) {
if (!this->m_config.complete_objects.value) {
gcode += this->m_label_objects.maybe_stop_instance();
}
this->m_label_objects.update(nullptr);
const std::pair<size_t, size_t> loops = loops_it->second; const std::pair<size_t, size_t> loops = loops_it->second;
this->set_origin(0., 0.); this->set_origin(0., 0.);
m_avoid_crossing_perimeters.use_external_mp(); m_avoid_crossing_perimeters.use_external_mp();
@ -2373,6 +2388,12 @@ LayerResult GCodeGenerator::process_layer(
// Extrude brim with the extruder of the 1st region. // Extrude brim with the extruder of the 1st region.
if (! m_brim_done) { if (! m_brim_done) {
if (!this->m_config.complete_objects.value) {
gcode += this->m_label_objects.maybe_stop_instance();
}
this->m_label_objects.update(nullptr);
this->set_origin(0., 0.); this->set_origin(0., 0.);
m_avoid_crossing_perimeters.use_external_mp(); m_avoid_crossing_perimeters.use_external_mp();
for (const ExtrusionEntity *ee : print.brim().entities) for (const ExtrusionEntity *ee : print.brim().entities)
@ -2382,8 +2403,7 @@ LayerResult GCodeGenerator::process_layer(
// Allow a straight travel move to the first object point. // Allow a straight travel move to the first object point.
m_avoid_crossing_perimeters.disable_once(); m_avoid_crossing_perimeters.disable_once();
} }
this->m_label_objects.update(first_instance);
std::vector<InstanceToPrint> instances_to_print = sort_print_object_instances(layers, ordering, single_object_instance_idx);
// We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature): // We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
bool is_anything_overridden = layer_tools.wiping_extrusions().is_anything_overridden(); bool is_anything_overridden = layer_tools.wiping_extrusions().is_anything_overridden();
@ -2507,7 +2527,7 @@ void GCodeGenerator::process_layer_single_object(
{ {
bool first = true; bool first = true;
// Delay layer initialization as many layers may not print with all extruders. // Delay layer initialization as many layers may not print with all extruders.
auto init_layer_delayed = [this, &print_instance, &layer_to_print, &first, &gcode]() { auto init_layer_delayed = [this, &print_instance, &layer_to_print, &first]() {
if (first) { if (first) {
first = false; first = false;
const PrintObject &print_object = print_instance.print_object; const PrintObject &print_object = print_instance.print_object;
@ -2518,12 +2538,11 @@ void GCodeGenerator::process_layer_single_object(
m_avoid_crossing_perimeters.init_layer(*m_layer); m_avoid_crossing_perimeters.init_layer(*m_layer);
// When starting a new object, use the external motion planner for the first travel move. // When starting a new object, use the external motion planner for the first travel move.
const Point &offset = print_object.instances()[print_instance.instance_id].shift; const Point &offset = print_object.instances()[print_instance.instance_id].shift;
GCode::PrintObjectInstance next_instance = {&print_object, int(print_instance.instance_id)};
if (m_current_instance != next_instance) const bool updated{m_label_objects.update(&print_instance.print_object.instances()[print_instance.instance_id])};
if (updated)
m_avoid_crossing_perimeters.use_external_mp_once(); m_avoid_crossing_perimeters.use_external_mp_once();
m_current_instance = next_instance;
this->set_origin(unscale(offset)); this->set_origin(unscale(offset));
gcode += m_label_objects.start_object(print_instance.print_object.instances()[print_instance.instance_id], GCode::LabelObjects::IncludeName::No);
} }
}; };
@ -2703,8 +2722,6 @@ void GCodeGenerator::process_layer_single_object(
} }
} }
} }
if (! first)
gcode += m_label_objects.stop_object(print_instance.print_object.instances()[print_instance.instance_id]);
} }
void GCodeGenerator::apply_print_config(const PrintConfig &print_config) void GCodeGenerator::apply_print_config(const PrintConfig &print_config)
@ -2788,6 +2805,10 @@ std::string GCodeGenerator::change_layer(
// Increment a progress bar indicator. // Increment a progress bar indicator.
gcode += m_writer.update_progress(++ m_layer_index, m_layer_count); gcode += m_writer.update_progress(++ m_layer_index, m_layer_count);
if (m_writer.multiple_extruders) {
gcode += m_label_objects.maybe_change_instance(m_writer);
}
if (!EXTRUDER_CONFIG(travel_ramping_lift) && EXTRUDER_CONFIG(retract_layer_change)) { if (!EXTRUDER_CONFIG(travel_ramping_lift) && EXTRUDER_CONFIG(retract_layer_change)) {
gcode += this->retract_and_wipe(); gcode += this->retract_and_wipe();
} else if (EXTRUDER_CONFIG(travel_ramping_lift) && !vase_mode){ } else if (EXTRUDER_CONFIG(travel_ramping_lift) && !vase_mode){
@ -2795,8 +2816,9 @@ std::string GCodeGenerator::change_layer(
std::optional{to_3d(this->point_to_gcode(*this->last_position), previous_layer_z)} : std::optional{to_3d(this->point_to_gcode(*this->last_position), previous_layer_z)} :
std::nullopt; std::nullopt;
gcode += GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change_Retraction_Start); gcode += GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change_Retraction_Start);
gcode += this->retract_and_wipe(); gcode += this->retract_and_wipe(false, false);
gcode += GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change_Retraction_End); gcode += GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change_Retraction_End);
gcode += m_writer.reset_e();
} }
Vec3d new_position = this->writer().get_position(); Vec3d new_position = this->writer().get_position();
@ -3069,7 +3091,7 @@ void GCodeGenerator::GCodeOutputStream::write_format(const char* format, ...)
va_end(args); va_end(args);
} }
std::string GCodeGenerator::travel_to_first_position(const Vec3crd& point, const double from_z) { std::string GCodeGenerator::travel_to_first_position(const Vec3crd& point, const double from_z, const std::function<std::string()>& insert_gcode) {
std::string gcode; std::string gcode;
const Vec3d gcode_point = to_3d(this->point_to_gcode(point.head<2>()), unscaled(point.z())); const Vec3d gcode_point = to_3d(this->point_to_gcode(point.head<2>()), unscaled(point.z()));
@ -3093,6 +3115,8 @@ std::string GCodeGenerator::travel_to_first_position(const Vec3crd& point, const
this->last_position = point.head<2>(); this->last_position = point.head<2>();
this->writer().update_position(gcode_point); this->writer().update_position(gcode_point);
gcode += insert_gcode();
std::string comment{"move to first layer point"}; std::string comment{"move to first layer point"};
gcode += this->writer().get_travel_to_xy_gcode(gcode_point.head<2>(), comment); gcode += this->writer().get_travel_to_xy_gcode(gcode_point.head<2>(), comment);
gcode += this->writer().get_travel_to_z_gcode(gcode_point.z(), comment); gcode += this->writer().get_travel_to_z_gcode(gcode_point.z(), comment);
@ -3124,15 +3148,23 @@ std::string GCodeGenerator::_extrude(
std::string gcode; std::string gcode;
const std::string_view description_bridge = path_attr.role.is_bridge() ? " (bridge)"sv : ""sv; const std::string_view description_bridge = path_attr.role.is_bridge() ? " (bridge)"sv : ""sv;
const bool has_active_instance{m_label_objects.has_active_instance()};
if (m_writer.multiple_extruders && has_active_instance) {
gcode += m_label_objects.maybe_change_instance(m_writer);
}
if (!m_current_layer_first_position) { if (!m_current_layer_first_position) {
const Vec3crd point = to_3d(path.front().point, scaled(this->m_last_layer_z)); const Vec3crd point = to_3d(path.front().point, scaled(this->m_last_layer_z));
gcode += this->travel_to_first_position(point, unscaled(point.z())); gcode += this->travel_to_first_position(point, unscaled(point.z()), [&](){
return m_writer.multiple_extruders ? "" : m_label_objects.maybe_change_instance(m_writer);
});
} else { } else {
// go to first point of extrusion path // go to first point of extrusion path
if (!this->last_position) { if (!this->last_position) {
const double z = this->m_last_layer_z; const double z = this->m_last_layer_z;
const std::string comment{"move to print after unknown position"}; const std::string comment{"move to print after unknown position"};
gcode += this->retract_and_wipe(); gcode += this->retract_and_wipe();
gcode += m_writer.multiple_extruders ? "" : m_label_objects.maybe_change_instance(m_writer);
gcode += this->m_writer.travel_to_xy(this->point_to_gcode(path.front().point), comment); gcode += this->m_writer.travel_to_xy(this->point_to_gcode(path.front().point), comment);
gcode += this->m_writer.get_travel_to_z_gcode(z, comment); gcode += this->m_writer.get_travel_to_z_gcode(z, comment);
} else if ( this->last_position != path.front().point) { } else if ( this->last_position != path.front().point) {
@ -3140,7 +3172,9 @@ std::string GCodeGenerator::_extrude(
comment += description; comment += description;
comment += description_bridge; comment += description_bridge;
comment += " point"; comment += " point";
const std::string travel_gcode{this->travel_to(*this->last_position, path.front().point, path_attr.role, comment)}; const std::string travel_gcode{this->travel_to(*this->last_position, path.front().point, path_attr.role, comment, [&](){
return m_writer.multiple_extruders ? "" : m_label_objects.maybe_change_instance(m_writer);
})};
gcode += travel_gcode; gcode += travel_gcode;
} }
} }
@ -3153,6 +3187,10 @@ std::string GCodeGenerator::_extrude(
gcode += "FIRST_UNRETRACT" + this->unretract(); gcode += "FIRST_UNRETRACT" + this->unretract();
} }
if (m_writer.multiple_extruders && !has_active_instance) {
gcode += m_label_objects.maybe_change_instance(m_writer);
}
if (!m_pending_pre_extrusion_gcode.empty()) { if (!m_pending_pre_extrusion_gcode.empty()) {
// There is G-Code that is due to be inserted before an extrusion starts. Insert it. // There is G-Code that is due to be inserted before an extrusion starts. Insert it.
gcode += m_pending_pre_extrusion_gcode; gcode += m_pending_pre_extrusion_gcode;
@ -3366,7 +3404,8 @@ std::string GCodeGenerator::_extrude(
std::string GCodeGenerator::generate_travel_gcode( std::string GCodeGenerator::generate_travel_gcode(
const Points3& travel, const Points3& travel,
const std::string& comment const std::string& comment,
const std::function<std::string()>& insert_gcode
) { ) {
std::string gcode; std::string gcode;
@ -3381,9 +3420,16 @@ std::string GCodeGenerator::generate_travel_gcode(
gcode += this->m_writer.set_travel_acceleration(acceleration); gcode += this->m_writer.set_travel_acceleration(acceleration);
Vec3d previous_point{this->point_to_gcode(travel.front())}; Vec3d previous_point{this->point_to_gcode(travel.front())};
for (const Vec3crd& point : travel) { bool already_inserted{false};
for (std::size_t i{0}; i < travel.size(); ++i) {
const Vec3crd& point{travel[i]};
const Vec3d gcode_point{this->point_to_gcode(point)}; const Vec3d gcode_point{this->point_to_gcode(point)};
if (travel.size() - i <= 2 && !already_inserted) {
gcode += insert_gcode();
already_inserted = true;
}
gcode += this->m_writer.travel_to_xyz(previous_point, gcode_point, comment); gcode += this->m_writer.travel_to_xyz(previous_point, gcode_point, comment);
this->last_position = point.head<2>(); this->last_position = point.head<2>();
previous_point = gcode_point; previous_point = gcode_point;
@ -3479,7 +3525,11 @@ Polyline GCodeGenerator::generate_travel_xy_path(
// This method accepts &point in print coordinates. // This method accepts &point in print coordinates.
std::string GCodeGenerator::travel_to( std::string GCodeGenerator::travel_to(
const Point &start_point, const Point &end_point, ExtrusionRole role, const std::string &comment const Point &start_point,
const Point &end_point,
ExtrusionRole role,
const std::string &comment,
const std::function<std::string()>& insert_gcode
) { ) {
// check whether a straight travel move would need retraction // check whether a straight travel move would need retraction
@ -3537,10 +3587,10 @@ std::string GCodeGenerator::travel_to(
) )
); );
return wipe_retract_gcode + generate_travel_gcode(travel, comment); return wipe_retract_gcode + generate_travel_gcode(travel, comment, insert_gcode);
} }
std::string GCodeGenerator::retract_and_wipe(bool toolchange) std::string GCodeGenerator::retract_and_wipe(bool toolchange, bool reset_e)
{ {
std::string gcode; std::string gcode;
@ -3558,7 +3608,10 @@ std::string GCodeGenerator::retract_and_wipe(bool toolchange)
methods even if we performed wipe, since this will ensure the entire retraction methods even if we performed wipe, since this will ensure the entire retraction
length is honored in case wipe path was too short. */ length is honored in case wipe path was too short. */
gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract(); gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract();
gcode += m_writer.reset_e();
if (reset_e) {
gcode += m_writer.reset_e();
}
return gcode; return gcode;
} }
@ -3589,8 +3642,13 @@ std::string GCodeGenerator::set_extruder(unsigned int extruder_id, double print_
return gcode; return gcode;
} }
std::string gcode{};
if (!this->m_config.complete_objects.value) {
gcode += this->m_label_objects.maybe_stop_instance();
}
// prepend retraction on the current extruder // prepend retraction on the current extruder
std::string gcode = this->retract_and_wipe(true); gcode += this->retract_and_wipe(true);
// Always reset the extrusion path, even if the tool change retract is set to zero. // Always reset the extrusion path, even if the tool change retract is set to zero.
m_wipe.reset_path(); m_wipe.reset_path();

View File

@ -319,7 +319,8 @@ private:
std::string extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache &smooth_path_cache); std::string extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache &smooth_path_cache);
std::string generate_travel_gcode( std::string generate_travel_gcode(
const Points3& travel, const Points3& travel,
const std::string& comment const std::string& comment,
const std::function<std::string()>& insert_gcode
); );
Polyline generate_travel_xy_path( Polyline generate_travel_xy_path(
const Point& start, const Point& start,
@ -331,14 +332,15 @@ private:
const Point &start_point, const Point &start_point,
const Point &end_point, const Point &end_point,
ExtrusionRole role, ExtrusionRole role,
const std::string &comment const std::string &comment,
const std::function<std::string()>& insert_gcode
); );
std::string travel_to_first_position(const Vec3crd& point, const double from_z); std::string travel_to_first_position(const Vec3crd& point, const double from_z, const std::function<std::string()>& insert_gcode);
bool needs_retraction(const Polyline &travel, ExtrusionRole role = ExtrusionRole::None); bool needs_retraction(const Polyline &travel, ExtrusionRole role = ExtrusionRole::None);
std::string retract_and_wipe(bool toolchange = false); std::string retract_and_wipe(bool toolchange = false, bool reset_e = true);
std::string unretract() { return m_writer.unretract(); } std::string unretract() { return m_writer.unretract(); }
std::string set_extruder(unsigned int extruder_id, double print_z); std::string set_extruder(unsigned int extruder_id, double print_z);
bool line_distancer_is_required(const std::vector<unsigned int>& extruder_ids); bool line_distancer_is_required(const std::vector<unsigned int>& extruder_ids);
@ -444,9 +446,6 @@ private:
bool m_second_layer_things_done; bool m_second_layer_things_done;
// G-code that is due to be written before the next extrusion // G-code that is due to be written before the next extrusion
std::string m_pending_pre_extrusion_gcode; std::string m_pending_pre_extrusion_gcode;
// Pointer to currently exporting PrintObject and instance index.
GCode::PrintObjectInstance m_current_instance;
bool m_silent_time_estimator_enabled; bool m_silent_time_estimator_enabled;
// Processor // Processor

View File

@ -1,6 +1,7 @@
#include "LabelObjects.hpp" #include "LabelObjects.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "GCode/GCodeWriter.hpp"
#include "Model.hpp" #include "Model.hpp"
#include "Print.hpp" #include "Print.hpp"
#include "TriangleMeshSlicer.hpp" #include "TriangleMeshSlicer.hpp"
@ -56,7 +57,7 @@ void LabelObjects::init(const SpanOfConstPtrs<PrintObject>& objects, LabelObject
for (const PrintObject* po : objects) for (const PrintObject* po : objects)
for (const PrintInstance& pi : po->instances()) for (const PrintInstance& pi : po->instances())
model_object_to_print_instances[pi.model_instance->get_object()].emplace_back(&pi); model_object_to_print_instances[pi.model_instance->get_object()].emplace_back(&pi);
// Now go through the map, assign a unique_id to each of the PrintInstances and get the indices of the // Now go through the map, assign a unique_id to each of the PrintInstances and get the indices of the
// respective ModelObject and ModelInstance so we can use them in the tags. This will maintain // respective ModelObject and ModelInstance so we can use them in the tags. This will maintain
// indices even in case that some instances are rotated (those end up in different PrintObjects) // indices even in case that some instances are rotated (those end up in different PrintObjects)
@ -111,7 +112,47 @@ void LabelObjects::init(const SpanOfConstPtrs<PrintObject>& objects, LabelObject
} }
} }
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 std::string LabelObjects::all_objects_header() const
{ {

View File

@ -12,39 +12,52 @@ enum GCodeFlavor : unsigned char;
enum class LabelObjectsStyle; enum class LabelObjectsStyle;
struct PrintInstance; struct PrintInstance;
class Print; class Print;
class GCodeWriter;
namespace GCode { namespace GCode {
class LabelObjects
class LabelObjects { {
public: 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 { enum class IncludeName {
No, No,
Yes Yes
}; };
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;
std::string start_object(const PrintInstance& print_instance, IncludeName include_name) const; std::string start_object(const PrintInstance& print_instance, IncludeName include_name) const;
std::string stop_object(const PrintInstance& print_instance) const; std::string stop_object(const PrintInstance& print_instance) const;
private: const PrintInstance* current_instance{nullptr};
struct LabelData { const PrintInstance* last_operation_instance{nullptr};
const PrintInstance* pi;
std::string name;
std::string center;
std::string polygon;
int unique_id;
};
LabelObjectsStyle m_label_objects_style; LabelObjectsStyle m_label_objects_style;
GCodeFlavor m_flavor; GCodeFlavor m_flavor;
std::vector<LabelData> m_label_data; std::vector<LabelData> m_label_data;
}; };
} // namespace GCode } // namespace GCode
} // namespace Slic3r } // namespace Slic3r

View File

@ -58,13 +58,14 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
|| will_go_down); // don't dig into the print || will_go_down); // don't dig into the print
if (should_travel_to_tower) { if (should_travel_to_tower) {
const Point xy_point = wipe_tower_point_to_object_point(gcodegen, start_pos); 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(); gcode += gcodegen.retract_and_wipe();
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
const std::string comment{"Travel to a Wipe Tower"}; const std::string comment{"Travel to a Wipe Tower"};
if (gcodegen.m_current_layer_first_position) { if (gcodegen.m_current_layer_first_position) {
if (gcodegen.last_position) { if (gcodegen.last_position) {
gcode += gcodegen.travel_to( gcode += gcodegen.travel_to(
*gcodegen.last_position, xy_point, ExtrusionRole::Mixed, comment *gcodegen.last_position, xy_point, ExtrusionRole::Mixed, comment, [](){return "";}
); );
} else { } else {
gcode += gcodegen.writer().travel_to_xy(gcodegen.point_to_gcode(xy_point), comment); 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 { } else {
const Vec3crd point = to_3d(xy_point, scaled(z)); 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, [](){return "";});
} }
gcode += gcodegen.unretract(); gcode += gcodegen.unretract();
} else { } else {
@ -268,7 +269,7 @@ std::string WipeTowerIntegration::finalize(GCodeGenerator &gcodegen)
if (std::abs(gcodegen.writer().get_position().z() - m_final_purge.print_z) > EPSILON) if (std::abs(gcodegen.writer().get_position().z() - m_final_purge.print_z) > EPSILON)
gcode += gcodegen.generate_travel_gcode( gcode += gcodegen.generate_travel_gcode(
{{gcodegen.last_position->x(), gcodegen.last_position->y(), scaled(m_final_purge.print_z)}}, {{gcodegen.last_position->x(), gcodegen.last_position->y(), scaled(m_final_purge.print_z)}},
"move to safe place for purging" "move to safe place for purging", [](){return "";}
); );
gcode += append_tcr(gcodegen, m_final_purge, -1); gcode += append_tcr(gcodegen, m_final_purge, -1);
return gcode; return gcode;

View File

@ -16,6 +16,7 @@ add_executable(${_TEST_NAME}_tests
test_gcode_travels.cpp test_gcode_travels.cpp
test_gcodefindreplace.cpp test_gcodefindreplace.cpp
test_gcodewriter.cpp test_gcodewriter.cpp
test_cancel_object.cpp
test_layers.cpp test_layers.cpp
test_model.cpp test_model.cpp
test_multi.cpp test_multi.cpp

View File

@ -0,0 +1,200 @@
#include <catch2/catch.hpp>
#include <sstream>
#include <fstream>
#include "libslic3r/GCode.hpp"
#include "test_data.hpp"
using namespace Slic3r;
using namespace Test;
constexpr bool debug_files{false};
std::string remove_object(const std::string &gcode, const int id) {
std::string result{gcode};
std::string start_token{"M486 S" + std::to_string(id) + "\n"};
std::string end_token{"M486 S-1\n"};
std::size_t start{result.find(start_token)};
while (start != std::string::npos) {
std::size_t end_token_start{result.find(end_token, start)};
std::size_t end{end_token_start + end_token.size()};
result.replace(start, end - start, "");
start = result.find(start_token);
}
return result;
}
TEST_CASE("Remove object sanity check", "[CancelObject]") {
// clang-format off
const std::string gcode{
"the\n"
"M486 S2\n"
"to delete\n"
"M486 S-1\n"
"kept\n"
"M486 S2\n"
"to also delete\n"
"M486 S-1\n"
"lines\n"
};
// clang-format on
const std::string result{remove_object(gcode, 2)};
// clang-format off
CHECK(result == std::string{
"the\n"
"kept\n"
"lines\n"
});
// clang-format on
}
void check_retraction(const std::string &gcode, double offset = 0.0) {
GCodeReader parser;
std::map<int, double> retracted;
unsigned count{0};
std::set<int> there_is_unretract;
int extruder_id{0};
parser.parse_buffer(
gcode,
[&](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) {
INFO("Line number: " + std::to_string(++count));
INFO("Extruder id: " + std::to_string(extruder_id));
if (!line.raw().empty() && line.raw().front() == 'T') {
extruder_id = std::stoi(std::string{line.raw().back()});
}
if (line.dist_XY(self) < std::numeric_limits<double>::epsilon()) {
if (line.has_e() && line.e() < 0) {
retracted[extruder_id] += line.e();
}
if (line.has_e() && line.e() > 0) {
INFO("Line: " + line.raw());
if (there_is_unretract.count(extruder_id) == 0) {
there_is_unretract.insert(extruder_id);
REQUIRE(retracted[extruder_id] + offset + line.e() == Approx(0.0));
} else {
REQUIRE(retracted[extruder_id] + line.e() == Approx(0.0));
}
retracted[extruder_id] = 0.0;
}
}
}
);
}
void add_object(
Model &model, const std::string &name, const int extruder, const Vec3d &offset = Vec3d::Zero()
) {
std::string extruder_id{std::to_string(extruder)};
ModelObject *object = model.add_object();
object->name = name;
ModelVolume *volume = object->add_volume(Test::mesh(Test::TestMesh::cube_20x20x20));
volume->set_material_id("material" + extruder_id);
volume->translate(offset);
DynamicPrintConfig config;
config.set_deserialize_strict({
{"extruder", extruder_id},
});
volume->config.assign_config(config);
object->add_instance();
object->ensure_on_bed();
}
class CancelObjectFixture
{
public:
CancelObjectFixture() {
config.set_deserialize_strict({
{"gcode_flavor", "marlin2"},
{"gcode_label_objects", "firmware"},
{"gcode_comments", "1"},
{"use_relative_e_distances", "1"},
{"wipe", "0"},
{"skirts", "0"},
});
add_object(two_cubes, "no_offset_cube", 0);
add_object(two_cubes, "offset_cube", 0, {30.0, 0.0, 0.0});
add_object(multimaterial_cubes, "no_offset_cube", 1);
add_object(multimaterial_cubes, "offset_cube", 2, {30.0, 0.0, 0.0});
retract_length = config.option<ConfigOptionFloats>("retract_length")->get_at(0);
retract_length_toolchange = config.option<ConfigOptionFloats>("retract_length_toolchange")
->get_at(0);
}
DynamicPrintConfig config{Slic3r::DynamicPrintConfig::full_print_config()};
Model two_cubes;
Model multimaterial_cubes;
double retract_length{};
double retract_length_toolchange{};
};
TEST_CASE_METHOD(CancelObjectFixture, "Single extruder", "[CancelObject]") {
Print print;
print.apply(two_cubes, config);
print.validate();
const std::string gcode{Test::gcode(print)};
if constexpr (debug_files) {
std::ofstream output{"single_extruder_two.gcode"};
output << gcode;
}
SECTION("One remaining") {
const std::string removed_object_gcode{remove_object(gcode, 0)};
REQUIRE(removed_object_gcode.find("M486 S1\n") != std::string::npos);
if constexpr (debug_files) {
std::ofstream output{"single_extruder_one.gcode"};
output << removed_object_gcode;
}
check_retraction(removed_object_gcode);
}
SECTION("All cancelled") {
const std::string removed_all_gcode{remove_object(remove_object(gcode, 0), 1)};
// First retraction is not compensated - set offset.
check_retraction(removed_all_gcode, retract_length);
}
}
TEST_CASE_METHOD(CancelObjectFixture, "Sequential print", "[CancelObject]") {
config.set_deserialize_strict({{"complete_objects", 1}});
Print print;
print.apply(two_cubes, config);
print.validate();
const std::string gcode{Test::gcode(print)};
if constexpr (debug_files) {
std::ofstream output{"sequential_print_two.gcode"};
output << gcode;
}
SECTION("One remaining") {
const std::string removed_object_gcode{remove_object(gcode, 0)};
REQUIRE(removed_object_gcode.find("M486 S1\n") != std::string::npos);
if constexpr (debug_files) {
std::ofstream output{"sequential_print_one.gcode"};
output << removed_object_gcode;
}
check_retraction(removed_object_gcode);
}
SECTION("All cancelled") {
const std::string removed_all_gcode{remove_object(remove_object(gcode, 0), 1)};
// First retraction is not compensated - set offset.
check_retraction(removed_all_gcode, retract_length);
}
}

View File

@ -149,7 +149,7 @@ TEST_CASE("Extrusion, travels, temeperatures", "[GCode]") {
INFO("Unexpected E argument"); INFO("Unexpected E argument");
CHECK(!line.has_e()); CHECK(!line.has_e());
if (line.has_z()) { if (line.has_z() && std::abs(line.dist_Z(self)) > 0) {
z_moves.emplace_back(line.z()); z_moves.emplace_back(line.z());
} }
if (line.has_x() || line.has_y()) { if (line.has_x() || line.has_y()) {