Merge branch 'lm_cancel_object'

This commit is contained in:
Lukas Matena 2023-09-26 11:24:29 +02:00
commit 9e3b9e43e3
7 changed files with 274 additions and 19 deletions

View File

@ -158,6 +158,8 @@ set(SLIC3R_SOURCES
GCode/ExtrusionProcessor.hpp GCode/ExtrusionProcessor.hpp
GCode/FindReplace.cpp GCode/FindReplace.cpp
GCode/FindReplace.hpp GCode/FindReplace.hpp
GCode/LabelObjects.cpp
GCode/LabelObjects.hpp
GCode/GCodeWriter.cpp GCode/GCodeWriter.cpp
GCode/GCodeWriter.hpp GCode/GCodeWriter.hpp
GCode/PostProcessor.cpp GCode/PostProcessor.cpp

View File

@ -28,6 +28,7 @@
#include "Exception.hpp" #include "Exception.hpp"
#include "ExtrusionEntity.hpp" #include "ExtrusionEntity.hpp"
#include "Geometry/ConvexHull.hpp" #include "Geometry/ConvexHull.hpp"
#include "GCode/LabelObjects.hpp"
#include "GCode/PrintExtents.hpp" #include "GCode/PrintExtents.hpp"
#include "GCode/Thumbnails.hpp" #include "GCode/Thumbnails.hpp"
#include "GCode/WipeTower.hpp" #include "GCode/WipeTower.hpp"
@ -102,7 +103,6 @@ namespace Slic3r {
gcode += '\n'; gcode += '\n';
} }
// Return true if tch_prefix is found in custom_gcode // Return true if tch_prefix is found in custom_gcode
static bool custom_gcode_changes_tool(const std::string& custom_gcode, const std::string& tch_prefix, unsigned next_extruder) static bool custom_gcode_changes_tool(const std::string& custom_gcode, const std::string& tch_prefix, unsigned next_extruder)
{ {
@ -1144,6 +1144,10 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
// Emit machine envelope limits for the Marlin firmware. // Emit machine envelope limits for the Marlin firmware.
this->print_machine_envelope(file, print); this->print_machine_envelope(file, print);
// Label all objects so printer knows about them since the start.
m_label_objects.init(print);
file.write(m_label_objects.all_objects_header());
// Update output variables after the extruders were initialized. // Update output variables after the extruders were initialized.
m_placeholder_parser_integration.init(m_writer); m_placeholder_parser_integration.init(m_writer);
// Let the start-up script prime the 1st printing tool. // Let the start-up script prime the 1st printing tool.
@ -2336,9 +2340,8 @@ void GCodeGenerator::process_layer_single_object(
const bool print_wipe_extrusions) const bool print_wipe_extrusions)
{ {
bool first = true; bool first = true;
int object_id = 0;
// 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, &object_id, &gcode]() { auto init_layer_delayed = [this, &print_instance, &layer_to_print, &first, &gcode]() {
if (first) { if (first) {
first = false; first = false;
const PrintObject &print_object = print_instance.print_object; const PrintObject &print_object = print_instance.print_object;
@ -2354,14 +2357,7 @@ void GCodeGenerator::process_layer_single_object(
m_avoid_crossing_perimeters.use_external_mp_once(); m_avoid_crossing_perimeters.use_external_mp_once();
m_last_obj_copy = this_object_copy; m_last_obj_copy = this_object_copy;
this->set_origin(unscale(offset)); this->set_origin(unscale(offset));
if (this->config().gcode_label_objects) { gcode += m_label_objects.start_object(print_instance.print_object.instances()[print_instance.instance_id], GCode::LabelObjects::IncludeName::No);
for (const PrintObject *po : print_object.print()->objects())
if (po == &print_object)
break;
else
++ object_id;
gcode += std::string("; printing object ") + print_object.model_object()->name + " id:" + std::to_string(object_id) + " copy " + std::to_string(print_instance.instance_id) + "\n";
}
} }
}; };
@ -2538,8 +2534,8 @@ void GCodeGenerator::process_layer_single_object(
} }
} }
} }
if (! first && this->config().gcode_label_objects) if (! first)
gcode += std::string("; stop printing object ") + print_object.model_object()->name + " id:" + std::to_string(object_id) + " copy " + std::to_string(print_instance.instance_id) + "\n"; 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)

View File

@ -28,6 +28,7 @@
#include "GCode/CoolingBuffer.hpp" #include "GCode/CoolingBuffer.hpp"
#include "GCode/FindReplace.hpp" #include "GCode/FindReplace.hpp"
#include "GCode/GCodeWriter.hpp" #include "GCode/GCodeWriter.hpp"
#include "GCode/LabelObjects.hpp"
#include "GCode/PressureEqualizer.hpp" #include "GCode/PressureEqualizer.hpp"
#include "GCode/RetractWhenCrossingPerimeters.hpp" #include "GCode/RetractWhenCrossingPerimeters.hpp"
#include "GCode/SmoothPath.hpp" #include "GCode/SmoothPath.hpp"
@ -353,6 +354,7 @@ private:
OozePrevention m_ooze_prevention; OozePrevention m_ooze_prevention;
GCode::Wipe m_wipe; GCode::Wipe m_wipe;
GCode::LabelObjects m_label_objects;
AvoidCrossingPerimeters m_avoid_crossing_perimeters; AvoidCrossingPerimeters m_avoid_crossing_perimeters;
JPSPathFinder m_avoid_crossing_curled_overhangs; JPSPathFinder m_avoid_crossing_curled_overhangs;
RetractWhenCrossingPerimeters m_retract_when_crossing_perimeters; RetractWhenCrossingPerimeters m_retract_when_crossing_perimeters;

View File

@ -0,0 +1,190 @@
#include "LabelObjects.hpp"
#include "ClipperUtils.hpp"
#include "Model.hpp"
#include "Print.hpp"
#include "TriangleMeshSlicer.hpp"
namespace Slic3r::GCode {
namespace {
Polygon instance_outline(const PrintInstance* pi)
{
ExPolygons outline;
const ModelObject* mo = pi->model_instance->get_object();
const ModelInstance* mi = pi->model_instance;
for (const ModelVolume *v : mo->volumes) {
Polygons vol_outline;
vol_outline = project_mesh(v->mesh().its,
mi->get_matrix() * v->get_matrix(),
[] {});
switch (v->type()) {
case ModelVolumeType::MODEL_PART: outline = union_ex(outline, vol_outline); break;
case ModelVolumeType::NEGATIVE_VOLUME: outline = diff_ex(outline, vol_outline); break;
default:;
}
}
// The projection may contain multiple polygons, which is not supported by Klipper.
// When that happens, calculate and use a 2d convex hull instead.
if (outline.size() == 1u)
return outline.front().contour;
else
return pi->model_instance->get_object()->convex_hull_2d(pi->model_instance->get_matrix());
}
}; // anonymous namespace
void LabelObjects::init(const Print& print)
{
m_label_objects_style = print.config().gcode_label_objects;
m_flavor = print.config().gcode_flavor;
if (m_label_objects_style == LabelObjectsStyle::Disabled)
return;
std::map<const ModelObject*, std::vector<const PrintInstance*>> model_object_to_print_instances;
// Iterate over all PrintObjects and their PrintInstances, collect PrintInstances which
// belong to the same ModelObject.
for (const PrintObject* po : print.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)
// or when some are out of bed (these ModelInstances have no corresponding PrintInstances).
int unique_id = 0;
for (const auto& [model_object, print_instances] : model_object_to_print_instances) {
const ModelObjectPtrs& model_objects = model_object->get_model()->objects;
int object_id = int(std::find(model_objects.begin(), model_objects.end(), model_object) - model_objects.begin());
for (const PrintInstance* const pi : print_instances) {
bool object_has_more_instances = print_instances.size() > 1u;
int instance_id = int(std::find(model_object->instances.begin(), model_object->instances.end(), pi->model_instance) - model_object->instances.begin());
// Now compose the name of the object and define whether indexing is 0 or 1-based.
std::string name = model_object->name;
if (m_label_objects_style == LabelObjectsStyle::Octoprint) {
// use zero-based indexing for objects and instances, as we always have done
name += " id:" + std::to_string(object_id) + " copy " + std::to_string(instance_id);
}
else if (m_label_objects_style == LabelObjectsStyle::Firmware) {
// use one-based indexing for objects and instances so indices match what we see in PrusaSlicer.
++object_id;
++instance_id;
if (object_has_more_instances)
name += " (Instance " + std::to_string(instance_id) + ")";
if (m_flavor == gcfKlipper) {
const std::string banned = "-. \r\n\v\t\f";
std::replace_if(name.begin(), name.end(), [&banned](char c) { return banned.find(c) != std::string::npos; }, '_');
}
}
m_label_data.emplace(pi, LabelData{name, unique_id});
++unique_id;
}
}
}
std::string LabelObjects::all_objects_header() const
{
if (m_label_objects_style == LabelObjectsStyle::Disabled)
return std::string();
std::string out;
// Let's sort the values according to unique_id so they are in the same order in which they were added.
std::vector<std::pair<const PrintInstance*, LabelData>> label_data_sorted;
for (const auto& pi_and_label : m_label_data)
label_data_sorted.emplace_back(pi_and_label);
std::sort(label_data_sorted.begin(), label_data_sorted.end(), [](const auto& ld1, const auto& ld2) { return ld1.second.unique_id < ld2.second.unique_id; });
out += "\n";
for (const auto& [print_instance, label] : label_data_sorted) {
if (m_flavor == gcfKlipper) {
char buffer[64];
out += "EXCLUDE_OBJECT_DEFINE NAME=" + label.name;
Polygon outline = instance_outline(print_instance);
assert(! outline.empty());
outline.douglas_peucker(50000.f);
Point center = outline.centroid();
std::snprintf(buffer, sizeof(buffer) - 1, " CENTER=%.3f,%.3f", unscale<float>(center[0]), unscale<float>(center[1]));
out += buffer + std::string(" POLYGON=[");
for (const Point& point : outline) {
std::snprintf(buffer, sizeof(buffer) - 1, "[%.3f,%.3f],", unscale<float>(point[0]), unscale<float>(point[1]));
out += buffer;
}
out.pop_back();
out += "]\n";
} else {
out += start_object(*print_instance, IncludeName::Yes);
out += stop_object(*print_instance);
}
}
out += "\n";
return out;
}
std::string LabelObjects::start_object(const PrintInstance& print_instance, IncludeName include_name) const
{
if (m_label_objects_style == LabelObjectsStyle::Disabled)
return std::string();
const LabelData& label = m_label_data.at(&print_instance);
std::string out;
if (m_label_objects_style == LabelObjectsStyle::Octoprint)
out += std::string("; printing object ") + label.name + "\n";
else if (m_label_objects_style == LabelObjectsStyle::Firmware) {
if (m_flavor == GCodeFlavor::gcfMarlinFirmware || m_flavor == GCodeFlavor::gcfMarlinLegacy || m_flavor == GCodeFlavor::gcfRepRapFirmware) {
out += std::string("M486 S") + std::to_string(label.unique_id) + "\n";
if (include_name == IncludeName::Yes) {
out += std::string("M486 A");
out += (m_flavor == GCodeFlavor::gcfRepRapFirmware ? (std::string("\"") + label.name + "\"") : label.name) + "\n";
}
} else if (m_flavor == gcfKlipper)
out += "EXCLUDE_OBJECT_START NAME=" + label.name + "\n";
else {
// Not supported by / implemented for the other firmware flavors.
}
}
return out;
}
std::string LabelObjects::stop_object(const PrintInstance& print_instance) const
{
if (m_label_objects_style == LabelObjectsStyle::Disabled)
return std::string();
const LabelData& label = m_label_data.at(&print_instance);
std::string out;
if (m_label_objects_style == LabelObjectsStyle::Octoprint)
out += std::string("; stop printing object ") + label.name + "\n";
else if (m_label_objects_style == LabelObjectsStyle::Firmware) {
if (m_flavor == GCodeFlavor::gcfMarlinFirmware || m_flavor == GCodeFlavor::gcfMarlinLegacy || m_flavor == GCodeFlavor::gcfRepRapFirmware)
out += std::string("M486 S-1\n");
else if (m_flavor ==gcfKlipper)
out += "EXCLUDE_OBJECT_END NAME=" + label.name + "\n";
else {
// Not supported by / implemented for the other firmware flavors.
}
}
return out;
}
} // namespace Slic3r::GCode

View File

@ -0,0 +1,42 @@
#ifndef slic3r_GCode_LabelObjects_hpp_
#define slic3r_GCode_LabelObjects_hpp_
namespace Slic3r {
enum GCodeFlavor : unsigned char;
enum class LabelObjectsStyle;
struct PrintInstance;
class Print;
namespace GCode {
class LabelObjects {
public:
enum class IncludeName {
No,
Yes
};
void init(const Print& print);
std::string all_objects_header() const;
std::string start_object(const PrintInstance& print_instance, IncludeName include_name) const;
std::string stop_object(const PrintInstance& print_instance) const;
private:
struct LabelData {
std::string name;
int unique_id;
};
LabelObjectsStyle m_label_objects_style;
GCodeFlavor m_flavor;
std::unordered_map<const PrintInstance*, LabelData> m_label_data;
};
} // namespace GCode
} // namespace Slic3r
#endif // slic3r_GCode_LabelObjects_hpp_

View File

@ -229,6 +229,13 @@ static const t_config_enum_values s_keys_map_DraftShield = {
}; };
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(DraftShield) CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(DraftShield)
static const t_config_enum_values s_keys_map_LabelObjectsStyle = {
{ "disabled", int(LabelObjectsStyle::Disabled) },
{ "octoprint", int(LabelObjectsStyle::Octoprint) },
{ "firmware", int(LabelObjectsStyle::Firmware) }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(LabelObjectsStyle)
static const t_config_enum_values s_keys_map_GCodeThumbnailsFormat = { static const t_config_enum_values s_keys_map_GCodeThumbnailsFormat = {
{ "PNG", int(GCodeThumbnailsFormat::PNG) }, { "PNG", int(GCodeThumbnailsFormat::PNG) },
{ "JPG", int(GCodeThumbnailsFormat::JPG) }, { "JPG", int(GCodeThumbnailsFormat::JPG) },
@ -1493,13 +1500,20 @@ void PrintConfigDef::init_fff_params()
def->mode = comExpert; def->mode = comExpert;
def->set_default_value(new ConfigOptionEnum<GCodeFlavor>(gcfRepRapSprinter)); def->set_default_value(new ConfigOptionEnum<GCodeFlavor>(gcfRepRapSprinter));
def = this->add("gcode_label_objects", coBool); def = this->add("gcode_label_objects", coEnum);
def->label = L("Label objects"); def->label = L("Label objects");
def->tooltip = L("Enable this to add comments into the G-Code labeling print moves with what object they belong to," def->tooltip = L("Selects whether labels should be exported at object boundaries and in what format.\n"
" which is useful for the Octoprint CancelObject plugin. This settings is NOT compatible with " " OctoPrint = comments to be consumed by OctoPrint CancelObject plugin.\n"
"Single Extruder Multi Material setup and Wipe into Object / Wipe into Infill."); " Firmware = firmware specific G-code (it will be chosen based on firmware flavor and it can end up to be empty).\n\n"
"This settings is NOT compatible with Single Extruder Multi Material setup and Wipe into Object / Wipe into Infill.");
def->set_enum<LabelObjectsStyle>({
{ "disabled", L("Disabled") },
{ "octoprint", L("OctoPrint comments") },
{ "firmware", L("Firmware-specific") }
});
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(0)); def->set_default_value(new ConfigOptionEnum<LabelObjectsStyle>(LabelObjectsStyle::Disabled));
def = this->add("gcode_substitutions", coStrings); def = this->add("gcode_substitutions", coStrings);
def->label = L("G-code substitutions"); def->label = L("G-code substitutions");
@ -4335,6 +4349,10 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
} else if (opt_key == "draft_shield" && (value == "1" || value == "0")) { } else if (opt_key == "draft_shield" && (value == "1" || value == "0")) {
// draft_shield used to be a bool, it was turned into an enum in PrusaSlicer 2.4.0. // draft_shield used to be a bool, it was turned into an enum in PrusaSlicer 2.4.0.
value = value == "1" ? "enabled" : "disabled"; value = value == "1" ? "enabled" : "disabled";
} else if (opt_key == "gcode_label_objects" && (value == "1" || value == "0")) {
// gcode_label_objects used to be a bool (the behavior was nothing or "octoprint"), it is
// and enum since PrusaSlicer 2.6.2.
value = value == "1" ? "octoprint" : "disabled";
} else if (opt_key == "octoprint_host") { } else if (opt_key == "octoprint_host") {
opt_key = "print_host"; opt_key = "print_host";
} else if (opt_key == "octoprint_cafile") { } else if (opt_key == "octoprint_cafile") {

View File

@ -147,6 +147,10 @@ enum DraftShield {
dsDisabled, dsLimited, dsEnabled dsDisabled, dsLimited, dsEnabled
}; };
enum class LabelObjectsStyle {
Disabled, Octoprint, Firmware
};
enum class PerimeterGeneratorType enum class PerimeterGeneratorType
{ {
// Classic perimeter generator using Clipper offsets with constant extrusion width. // Classic perimeter generator using Clipper offsets with constant extrusion width.
@ -183,6 +187,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLAPillarConnectionMode)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLASupportTreeType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLASupportTreeType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BrimType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BrimType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(DraftShield) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(DraftShield)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(LabelObjectsStyle)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeThumbnailsFormat) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeThumbnailsFormat)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ForwardCompatibilitySubstitutionRule) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ForwardCompatibilitySubstitutionRule)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType)
@ -729,7 +734,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloats, filament_multitool_ramming_flow)) ((ConfigOptionFloats, filament_multitool_ramming_flow))
((ConfigOptionBool, gcode_comments)) ((ConfigOptionBool, gcode_comments))
((ConfigOptionEnum<GCodeFlavor>, gcode_flavor)) ((ConfigOptionEnum<GCodeFlavor>, gcode_flavor))
((ConfigOptionBool, gcode_label_objects)) ((ConfigOptionEnum<LabelObjectsStyle>, gcode_label_objects))
// Triples of strings: "search pattern", "replace with pattern", "attribs" // Triples of strings: "search pattern", "replace with pattern", "attribs"
// where "attribs" are one of: // where "attribs" are one of:
// r - regular expression // r - regular expression