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_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();

View File

@ -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

View File

@ -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
{

View File

@ -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

View File

@ -58,13 +58,14 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
|| will_go_down); // don't dig into the print
if (should_travel_to_tower) {
const Point xy_point = wipe_tower_point_to_object_point(gcodegen, start_pos);
gcode += gcodegen.m_label_objects.maybe_stop_instance();
gcode += gcodegen.retract_and_wipe();
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
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;

View File

@ -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

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");
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()) {