mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-13 06:39:07 +08:00
Merge branch 'ms_cancel_object' into master_27x
This commit is contained in:
commit
ea65285999
@ -576,7 +576,6 @@ GCodeGenerator::GCodeGenerator(const Print* print) :
|
||||
m_brim_done(false),
|
||||
m_second_layer_things_done(false),
|
||||
m_silent_time_estimator_enabled(false),
|
||||
m_current_instance({nullptr, -1}),
|
||||
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_avoid_crossing_perimeters.use_external_mp_once();
|
||||
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;
|
||||
// Disable motion planner when traveling to first object point.
|
||||
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;
|
||||
prev_object = &object;
|
||||
}
|
||||
|
||||
file.write(m_label_objects.maybe_stop_instance());
|
||||
} else {
|
||||
// Sort layers by Z.
|
||||
// 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.
|
||||
this->process_layers(print, tool_ordering, print_object_instances_ordering, layers_to_print,
|
||||
smooth_path_cache_global, file);
|
||||
file.write(m_label_objects.maybe_stop_instance());
|
||||
if (m_wipe_tower)
|
||||
// Purge the extruder, pull out the active filament.
|
||||
file.write(m_wipe_tower->finalize(*this));
|
||||
@ -2206,6 +2211,11 @@ LayerResult GCodeGenerator::process_layer(
|
||||
bool first_layer = layer.id() == 0;
|
||||
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.
|
||||
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 (!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;
|
||||
this->set_origin(0., 0.);
|
||||
m_avoid_crossing_perimeters.use_external_mp();
|
||||
@ -2373,6 +2388,12 @@ LayerResult GCodeGenerator::process_layer(
|
||||
|
||||
// Extrude brim with the extruder of the 1st region.
|
||||
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.);
|
||||
m_avoid_crossing_perimeters.use_external_mp();
|
||||
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.
|
||||
m_avoid_crossing_perimeters.disable_once();
|
||||
}
|
||||
|
||||
std::vector<InstanceToPrint> instances_to_print = sort_print_object_instances(layers, ordering, single_object_instance_idx);
|
||||
this->m_label_objects.update(first_instance);
|
||||
|
||||
// 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();
|
||||
@ -2507,7 +2527,7 @@ void GCodeGenerator::process_layer_single_object(
|
||||
{
|
||||
bool first = true;
|
||||
// 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) {
|
||||
first = false;
|
||||
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);
|
||||
// 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;
|
||||
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_current_instance = next_instance;
|
||||
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)
|
||||
@ -2788,6 +2805,10 @@ std::string GCodeGenerator::change_layer(
|
||||
// Increment a progress bar indicator.
|
||||
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)) {
|
||||
gcode += this->retract_and_wipe();
|
||||
} 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::nullopt;
|
||||
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 += m_writer.reset_e();
|
||||
}
|
||||
|
||||
Vec3d new_position = this->writer().get_position();
|
||||
@ -3069,7 +3091,7 @@ void GCodeGenerator::GCodeOutputStream::write_format(const char* format, ...)
|
||||
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;
|
||||
|
||||
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->writer().update_position(gcode_point);
|
||||
|
||||
gcode += insert_gcode();
|
||||
|
||||
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_z_gcode(gcode_point.z(), comment);
|
||||
@ -3124,15 +3148,23 @@ std::string GCodeGenerator::_extrude(
|
||||
std::string gcode;
|
||||
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) {
|
||||
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 {
|
||||
// go to first point of extrusion path
|
||||
if (!this->last_position) {
|
||||
const double z = this->m_last_layer_z;
|
||||
const std::string comment{"move to print after unknown position"};
|
||||
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.get_travel_to_z_gcode(z, comment);
|
||||
} else if ( this->last_position != path.front().point) {
|
||||
@ -3140,7 +3172,9 @@ std::string GCodeGenerator::_extrude(
|
||||
comment += description;
|
||||
comment += description_bridge;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -3153,6 +3187,10 @@ std::string GCodeGenerator::_extrude(
|
||||
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()) {
|
||||
// There is G-Code that is due to be inserted before an extrusion starts. Insert it.
|
||||
gcode += m_pending_pre_extrusion_gcode;
|
||||
@ -3366,7 +3404,8 @@ std::string GCodeGenerator::_extrude(
|
||||
|
||||
std::string GCodeGenerator::generate_travel_gcode(
|
||||
const Points3& travel,
|
||||
const std::string& comment
|
||||
const std::string& comment,
|
||||
const std::function<std::string()>& insert_gcode
|
||||
) {
|
||||
std::string gcode;
|
||||
|
||||
@ -3381,9 +3420,16 @@ std::string GCodeGenerator::generate_travel_gcode(
|
||||
gcode += this->m_writer.set_travel_acceleration(acceleration);
|
||||
|
||||
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)};
|
||||
|
||||
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);
|
||||
this->last_position = point.head<2>();
|
||||
previous_point = gcode_point;
|
||||
@ -3479,7 +3525,11 @@ Polyline GCodeGenerator::generate_travel_xy_path(
|
||||
|
||||
// This method accepts &point in print coordinates.
|
||||
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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
length is honored in case wipe path was too short. */
|
||||
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;
|
||||
}
|
||||
@ -3589,8 +3642,13 @@ std::string GCodeGenerator::set_extruder(unsigned int extruder_id, double print_
|
||||
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
|
||||
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.
|
||||
m_wipe.reset_path();
|
||||
|
@ -319,7 +319,8 @@ private:
|
||||
std::string extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache &smooth_path_cache);
|
||||
std::string generate_travel_gcode(
|
||||
const Points3& travel,
|
||||
const std::string& comment
|
||||
const std::string& comment,
|
||||
const std::function<std::string()>& insert_gcode
|
||||
);
|
||||
Polyline generate_travel_xy_path(
|
||||
const Point& start,
|
||||
@ -331,14 +332,15 @@ private:
|
||||
const Point &start_point,
|
||||
const Point &end_point,
|
||||
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);
|
||||
|
||||
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 set_extruder(unsigned int extruder_id, double print_z);
|
||||
bool line_distancer_is_required(const std::vector<unsigned int>& extruder_ids);
|
||||
@ -444,9 +446,6 @@ private:
|
||||
bool m_second_layer_things_done;
|
||||
// G-code that is due to be written before the next extrusion
|
||||
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;
|
||||
|
||||
// Processor
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "LabelObjects.hpp"
|
||||
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "GCode/GCodeWriter.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "TriangleMeshSlicer.hpp"
|
||||
@ -56,7 +57,7 @@ void LabelObjects::init(const SpanOfConstPtrs<PrintObject>& objects, LabelObject
|
||||
for (const PrintObject* po : objects)
|
||||
for (const PrintInstance& pi : po->instances())
|
||||
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
|
||||
// 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)
|
||||
@ -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
|
||||
{
|
||||
|
@ -12,39 +12,52 @@ enum GCodeFlavor : unsigned char;
|
||||
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 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 stop_object(const PrintInstance& print_instance) const;
|
||||
|
||||
private:
|
||||
struct LabelData {
|
||||
const PrintInstance* pi;
|
||||
std::string name;
|
||||
std::string center;
|
||||
std::string polygon;
|
||||
int unique_id;
|
||||
};
|
||||
const PrintInstance* current_instance{nullptr};
|
||||
const PrintInstance* last_operation_instance{nullptr};
|
||||
|
||||
LabelObjectsStyle m_label_objects_style;
|
||||
GCodeFlavor m_flavor;
|
||||
std::vector<LabelData> m_label_data;
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace GCode
|
||||
} // namespace Slic3r
|
||||
|
||||
|
@ -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();
|
||||
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, [](){return "";});
|
||||
}
|
||||
gcode += gcodegen.unretract();
|
||||
} 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)
|
||||
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"
|
||||
"move to safe place for purging", [](){return "";}
|
||||
);
|
||||
gcode += append_tcr(gcodegen, m_final_purge, -1);
|
||||
return gcode;
|
||||
|
@ -16,6 +16,7 @@ add_executable(${_TEST_NAME}_tests
|
||||
test_gcode_travels.cpp
|
||||
test_gcodefindreplace.cpp
|
||||
test_gcodewriter.cpp
|
||||
test_cancel_object.cpp
|
||||
test_layers.cpp
|
||||
test_model.cpp
|
||||
test_multi.cpp
|
||||
|
200
tests/fff_print/test_cancel_object.cpp
Normal file
200
tests/fff_print/test_cancel_object.cpp
Normal 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);
|
||||
}
|
||||
}
|
@ -149,7 +149,7 @@ TEST_CASE("Extrusion, travels, temeperatures", "[GCode]") {
|
||||
INFO("Unexpected E argument");
|
||||
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());
|
||||
}
|
||||
if (line.has_x() || line.has_y()) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user