diff --git a/resources/icons/export_to_sd.svg b/resources/icons/export_to_sd.svg
new file mode 100644
index 000000000..516cec435
--- /dev/null
+++ b/resources/icons/export_to_sd.svg
@@ -0,0 +1,168 @@
+
+
diff --git a/resources/localization/PrusaSlicer.pot b/resources/localization/PrusaSlicer.pot
index c5bcaa3bd..77539a7a3 100644
--- a/resources/localization/PrusaSlicer.pot
+++ b/resources/localization/PrusaSlicer.pot
@@ -4914,7 +4914,7 @@ msgid "Cannot overwrite an external profile."
msgstr ""
#: src/slic3r/GUI/Tab.cpp:3038
-msgid "Preset with name \"%1%\" already exist."
+msgid "Preset with name \"%1%\" already exists."
msgstr ""
#: src/slic3r/GUI/Tab.cpp:3039
diff --git a/resources/localization/list.txt b/resources/localization/list.txt
index 93ee2f441..fc33097df 100644
--- a/resources/localization/list.txt
+++ b/resources/localization/list.txt
@@ -46,6 +46,7 @@ src/slic3r/GUI/Tab.hpp
src/slic3r/GUI/UpdateDialogs.cpp
src/slic3r/GUI/WipeTowerDialog.cpp
src/slic3r/GUI/wxExtensions.cpp
+src/slic3r/GUI/DoubleSlider.cpp
src/slic3r/GUI/ExtruderSequenceDialog.cpp
src/slic3r/Utils/Duet.cpp
src/slic3r/Utils/OctoPrint.cpp
diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx
index c451bb8bf..9ac22c59d 100644
--- a/resources/profiles/PrusaResearch.idx
+++ b/resources/profiles/PrusaResearch.idx
@@ -1,3 +1,8 @@
+min_slic3r_version = 2.2.0-alpha3
+1.1.1-alpha3 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer.
+# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer,
+# so they will see the print bed.
+max_slic3r_version = 2.2.0-alpha2
min_slic3r_version = 2.2.0-alpha0
1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles.
1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0
diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini
index 4f5c0482b..aa4b7cff0 100644
--- a/resources/profiles/PrusaResearch.ini
+++ b/resources/profiles/PrusaResearch.ini
@@ -5,7 +5,7 @@
name = Prusa Research
# Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded.
-config_version = 1.1.1-alpha2
+config_version = 1.1.1-alpha3
# Where to get the updates from?
config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
changelog_url = http://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 54ebf33b9..11996af1a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -84,7 +84,7 @@ endif (MINGW)
if (NOT WIN32)
# Binary name on unix like systems (OSX, Linux)
- set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
+ set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
endif ()
target_link_libraries(PrusaSlicer libslic3r cereal)
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index d92bb4cac..6d5b3e5f5 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -119,6 +119,8 @@ add_library(libslic3r STATIC
Line.hpp
Model.cpp
Model.hpp
+ CustomGCode.cpp
+ CustomGCode.hpp
Arrange.hpp
Arrange.cpp
MotionPlanner.cpp
diff --git a/src/libslic3r/CustomGCode.cpp b/src/libslic3r/CustomGCode.cpp
new file mode 100644
index 000000000..7c505c978
--- /dev/null
+++ b/src/libslic3r/CustomGCode.cpp
@@ -0,0 +1,72 @@
+#include "CustomGCode.hpp"
+#include "Config.hpp"
+#include "GCode/PreviewData.hpp"
+#include "GCodeWriter.hpp"
+
+namespace Slic3r {
+
+namespace CustomGCode {
+
+// If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer),
+// and if CustomGCode::Info.gcodes is empty (there is no color print data available in a new format
+// then CustomGCode::Info.gcodes should be updated considering this option.
+extern void update_custom_gcode_per_print_z_from_config(Info& info, DynamicPrintConfig* config)
+{
+ auto *colorprint_heights = config->option("colorprint_heights");
+ if (colorprint_heights == nullptr)
+ return;
+ if (info.gcodes.empty() && ! colorprint_heights->values.empty()) {
+ // Convert the old colorprint_heighs only if there is no equivalent data in a new format.
+ const std::vector& colors = GCodePreviewData::ColorPrintColors();
+ const auto& colorprint_values = colorprint_heights->values;
+ info.gcodes.clear();
+ info.gcodes.reserve(colorprint_values.size());
+ int i = 0;
+ for (auto val : colorprint_values)
+ info.gcodes.emplace_back(Item{ val, ColorChangeCode, 1, colors[(++i)%7] });
+
+ info.mode = SingleExtruder;
+ }
+
+ // The "colorprint_heights" config value has been deprecated. At this point of time it has been converted
+ // to a new format and therefore it shall be erased.
+ config->erase("colorprint_heights");
+}
+
+// If information for custom Gcode per print Z was imported from older Slicer, mode will be undefined.
+// So, we should set CustomGCode::Info.mode should be updated considering code values from items.
+extern void check_mode_for_custom_gcode_per_print_z(Info& info)
+{
+ if (info.mode != Undef)
+ return;
+
+ bool is_single_extruder = true;
+ for (auto item : info.gcodes)
+ {
+ if (item.gcode == ToolChangeCode) {
+ info.mode = MultiAsSingle;
+ return;
+ }
+ if (item.gcode == ColorChangeCode && item.extruder > 1)
+ is_single_extruder = false;
+ }
+
+ info.mode = is_single_extruder ? SingleExtruder : MultiExtruder;
+}
+
+// Return pairs of sorted by increasing print_z from custom_gcode_per_print_z.
+// print_z corresponds to the first layer printed with the new extruder.
+std::vector> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders)
+{
+ std::vector> custom_tool_changes;
+ for (const Item& custom_gcode : custom_gcode_per_print_z.gcodes)
+ if (custom_gcode.gcode == ToolChangeCode) {
+ // If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
+ custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder));
+ }
+ return custom_tool_changes;
+}
+
+} // namespace CustomGCode
+
+} // namespace Slic3r
diff --git a/src/libslic3r/CustomGCode.hpp b/src/libslic3r/CustomGCode.hpp
new file mode 100644
index 000000000..5ab4c76ef
--- /dev/null
+++ b/src/libslic3r/CustomGCode.hpp
@@ -0,0 +1,86 @@
+#ifndef slic3r_CustomGCode_hpp_
+#define slic3r_CustomGCode_hpp_
+
+#include
+#include
+
+namespace Slic3r {
+
+class DynamicPrintConfig;
+
+// Additional Codes which can be set by user using DoubleSlider
+static constexpr char ColorChangeCode[] = "M600";
+static constexpr char PausePrintCode[] = "M601";
+static constexpr char ToolChangeCode[] = "tool_change";
+
+namespace CustomGCode {
+
+struct Item
+{
+ bool operator<(const Item& rhs) const { return this->print_z < rhs.print_z; }
+ bool operator==(const Item& rhs) const
+ {
+ return (rhs.print_z == this->print_z ) &&
+ (rhs.gcode == this->gcode ) &&
+ (rhs.extruder == this->extruder ) &&
+ (rhs.color == this->color );
+ }
+ bool operator!=(const Item& rhs) const { return ! (*this == rhs); }
+
+ double print_z;
+ std::string gcode;
+ int extruder; // Informative value for ColorChangeCode and ToolChangeCode
+ // "gcode" == ColorChangeCode => M600 will be applied for "extruder" extruder
+ // "gcode" == ToolChangeCode => for whole print tool will be switched to "extruder" extruder
+ std::string color; // if gcode is equal to PausePrintCode,
+ // this field is used for save a short message shown on Printer display
+};
+
+enum Mode
+{
+ Undef,
+ SingleExtruder, // Single extruder printer preset is selected
+ MultiAsSingle, // Multiple extruder printer preset is selected, but
+ // this mode works just for Single extruder print
+ // (For all print from objects settings is used just one extruder)
+ MultiExtruder // Multiple extruder printer preset is selected
+};
+
+// string anlogue of custom_code_per_height mode
+static constexpr char SingleExtruderMode[] = "SingleExtruder";
+static constexpr char MultiAsSingleMode [] = "MultiAsSingle";
+static constexpr char MultiExtruderMode [] = "MultiExtruder";
+
+struct Info
+{
+ Mode mode = Undef;
+ std::vector- gcodes;
+
+ bool operator==(const Info& rhs) const
+ {
+ return (rhs.mode == this->mode ) &&
+ (rhs.gcodes == this->gcodes );
+ }
+ bool operator!=(const Info& rhs) const { return !(*this == rhs); }
+};
+
+// If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer),
+// and if CustomGCode::Info.gcodes is empty (there is no color print data available in a new format
+// then CustomGCode::Info.gcodes should be updated considering this option.
+extern void update_custom_gcode_per_print_z_from_config(Info& info, DynamicPrintConfig* config);
+
+// If information for custom Gcode per print Z was imported from older Slicer, mode will be undefined.
+// So, we should set CustomGCode::Info.mode should be updated considering code values from items.
+extern void check_mode_for_custom_gcode_per_print_z(Info& info);
+
+// Return pairs of sorted by increasing print_z from custom_gcode_per_print_z.
+// print_z corresponds to the first layer printed with the new extruder.
+std::vector> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders);
+
+} // namespace CustomGCode
+
+} // namespace Slic3r
+
+
+
+#endif /* slic3r_CustomGCode_hpp_ */
diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index de7b26f04..45f39c787 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -1191,6 +1191,14 @@ namespace Slic3r {
for (const auto& code : code_tree)
{
+ if (code.first == "mode")
+ {
+ pt::ptree tree = code.second;
+ std::string mode = tree.get(".value");
+ m_model->custom_gcode_per_print_z.mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder :
+ mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle :
+ CustomGCode::Mode::MultiExtruder;
+ }
if (code.first != "code")
continue;
pt::ptree tree = code.second;
@@ -1199,7 +1207,7 @@ namespace Slic3r {
int extruder = tree.get (".extruder" );
std::string color = tree.get (".color" );
- m_model->custom_gcode_per_print_z.gcodes.push_back(Model::CustomGCode{print_z, gcode, extruder, color}) ;
+ m_model->custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, gcode, extruder, color}) ;
}
}
}
@@ -2767,7 +2775,7 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
pt::ptree tree;
pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", "");
- for (const Model::CustomGCode& code : model.custom_gcode_per_print_z.gcodes)
+ for (const CustomGCode::Item& code : model.custom_gcode_per_print_z.gcodes)
{
pt::ptree& code_tree = main_tree.add("code", "");
// store minX and maxZ
@@ -2775,7 +2783,13 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
code_tree.put(".gcode" , code.gcode );
code_tree.put(".extruder" , code.extruder );
code_tree.put(".color" , code.color );
- }
+ }
+
+ pt::ptree& mode_tree = main_tree.add("mode", "");
+ // store mode of a custom_gcode_per_print_z
+ mode_tree.put(".value", model.custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
+ model.custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
+ CustomGCode::MultiExtruderMode);
if (!tree.empty())
{
diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp
index 7041956ba..ede4f403b 100644
--- a/src/libslic3r/Format/AMF.cpp
+++ b/src/libslic3r/Format/AMF.cpp
@@ -13,6 +13,7 @@
#include "../Utils.hpp"
#include "../I18N.hpp"
#include "../Geometry.hpp"
+#include "../CustomGCode.hpp"
#include "AMF.hpp"
@@ -156,6 +157,7 @@ struct AMFParserContext
NODE_TYPE_PRINTABLE, // amf/constellation/instance/mirrorz
NODE_TYPE_CUSTOM_GCODE, // amf/custom_code_per_height
NODE_TYPE_GCODE_PER_HEIGHT, // amf/custom_code_per_height/code
+ NODE_TYPE_CUSTOM_GCODE_MODE, // amf/custom_code_per_height/mode
NODE_TYPE_METADATA, // anywhere under amf/*/metadata
};
@@ -308,12 +310,18 @@ void AMFParserContext::startElement(const char *name, const char **atts)
else
this->stop();
}
- else if (strcmp(name, "code") == 0 && m_path[1] == NODE_TYPE_CUSTOM_GCODE) {
- node_type_new = NODE_TYPE_GCODE_PER_HEIGHT;
- m_value[0] = get_attribute(atts, "height");
- m_value[1] = get_attribute(atts, "gcode");
- m_value[2] = get_attribute(atts, "extruder");
- m_value[3] = get_attribute(atts, "color");
+ else if (m_path[1] == NODE_TYPE_CUSTOM_GCODE) {
+ if (strcmp(name, "code") == 0) {
+ node_type_new = NODE_TYPE_GCODE_PER_HEIGHT;
+ m_value[0] = get_attribute(atts, "print_z");
+ m_value[1] = get_attribute(atts, "gcode");
+ m_value[2] = get_attribute(atts, "extruder");
+ m_value[3] = get_attribute(atts, "color");
+ }
+ else if (strcmp(name, "mode") == 0) {
+ node_type_new = NODE_TYPE_CUSTOM_GCODE_MODE;
+ m_value[0] = get_attribute(atts, "value");
+ }
}
break;
case 3:
@@ -632,18 +640,29 @@ void AMFParserContext::endElement(const char * /* name */)
break;
case NODE_TYPE_GCODE_PER_HEIGHT: {
- double height = double(atof(m_value[0].c_str()));
+ double print_z = double(atof(m_value[0].c_str()));
const std::string& gcode = m_value[1];
int extruder = atoi(m_value[2].c_str());
const std::string& color = m_value[3];
- m_model.custom_gcode_per_print_z.gcodes.push_back(Model::CustomGCode{height, gcode, extruder, color});
+ m_model.custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, gcode, extruder, color});
for (std::string& val: m_value)
val.clear();
break;
}
+ case NODE_TYPE_CUSTOM_GCODE_MODE: {
+ const std::string& mode = m_value[0];
+
+ m_model.custom_gcode_per_print_z.mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder :
+ mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle :
+ CustomGCode::Mode::MultiExtruder;
+ for (std::string& val: m_value)
+ val.clear();
+ break;
+ }
+
case NODE_TYPE_METADATA:
if ((m_config != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0)
m_config->load_from_gcode_string(m_value[1].c_str());
@@ -1237,16 +1256,23 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
pt::ptree& main_tree = tree.add("custom_gcodes_per_height", "");
- for (const Model::CustomGCode& code : model->custom_gcode_per_print_z.gcodes)
+ for (const CustomGCode::Item& code : model->custom_gcode_per_print_z.gcodes)
{
pt::ptree& code_tree = main_tree.add("code", "");
- // store minX and maxZ
+ // store custom_gcode_per_print_z gcodes information
code_tree.put(".print_z" , code.print_z );
code_tree.put(".gcode" , code.gcode );
code_tree.put(".extruder" , code.extruder );
code_tree.put(".color" , code.color );
}
+ pt::ptree& mode_tree = main_tree.add("mode", "");
+ // store mode of a custom_gcode_per_print_z
+ mode_tree.put(".value",
+ model->custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
+ model->custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ?
+ CustomGCode::MultiAsSingleMode : CustomGCode::MultiExtruderMode);
+
if (!tree.empty())
{
std::ostringstream oss;
@@ -1259,6 +1285,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
// Post processing("beautification") of the output string
boost::replace_all(out, ">
\n \n <", ">\n<");
stream << out << "\n";
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 517b5281b..cc83461d3 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -8,6 +8,7 @@
#include "GCode/WipeTower.hpp"
#include "ShortestPath.hpp"
#include "Utils.hpp"
+#include "libslic3r.h"
#include
#include
@@ -164,12 +165,12 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP
cnt = (cnt + 1) / 2;
}
// And collect copies of the objects.
- for (const Point © : object->copies()) {
+ for (const PrintInstance &instance : object->instances()) {
// All the layers were reduced to the 1st item of polygons_per_layer.
size_t i = islands.size();
polygons_append(islands, polygons_per_layer.front());
for (; i < islands.size(); ++ i)
- islands[i].translate(copy);
+ islands[i].translate(instance.shift);
}
}
return islands;
@@ -199,7 +200,7 @@ std::string OozePrevention::pre_toolchange(GCode &gcodegen)
if (gcodegen.config().standby_temperature_delta.value != 0) {
// we assume that heating is always slower than cooling, so no need to block
gcode += gcodegen.writer().set_temperature
- (this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false);
+ (this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false, gcodegen.writer().extruder()->id());
}
return gcode;
@@ -208,7 +209,7 @@ std::string OozePrevention::pre_toolchange(GCode &gcodegen)
std::string OozePrevention::post_toolchange(GCode &gcodegen)
{
return (gcodegen.config().standby_temperature_delta.value != 0) ?
- gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true) :
+ gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true, gcodegen.writer().extruder()->id()) :
std::string();
}
@@ -1086,6 +1087,41 @@ namespace DoExport {
}
}
+// Sort the PrintObjects by their increasing Z, likely useful for avoiding colisions on Deltas during sequential prints.
+static inline std::vector sort_object_instances_by_max_z(const Print &print)
+{
+ std::vector objects(print.objects().begin(), print.objects().end());
+ std::sort(objects.begin(), objects.end(), [](const PrintObject *po1, const PrintObject *po2) { return po1->size(2) < po2->size(2); });
+ std::vector instances;
+ instances.reserve(objects.size());
+ for (const PrintObject *object : objects)
+ for (size_t i = 0; i < object->instances().size(); ++ i)
+ instances.emplace_back(&object->instances()[i]);
+ return instances;
+}
+
+// Produce a vector of PrintObjects in the order of their respective ModelObjects in print.model().
+static inline std::vector sort_object_instances_by_model_order(const Print &print)
+{
+ // Build up map from ModelInstance* to PrintInstance*
+ std::vector> model_instance_to_print_instance;
+ model_instance_to_print_instance.reserve(print.num_object_instances());
+ for (const PrintObject *print_object : print.objects())
+ for (const PrintInstance &print_instance : print_object->instances())
+ model_instance_to_print_instance.emplace_back(print_instance.model_instance, &print_instance);
+ std::sort(model_instance_to_print_instance.begin(), model_instance_to_print_instance.end(), [](auto &l, auto &r) { return l.first < r.first; });
+
+ std::vector instances;
+ instances.reserve(model_instance_to_print_instance.size());
+ for (const ModelObject *model_object : print.model().objects)
+ for (const ModelInstance *model_instance : model_object->instances) {
+ auto it = std::lower_bound(model_instance_to_print_instance.begin(), model_instance_to_print_instance.end(), std::make_pair(model_instance, nullptr), [](auto &l, auto &r) { return l.first < r.first; });
+ if (it != model_instance_to_print_instance.end() && it->first == model_instance)
+ instances.emplace_back(it->second);
+ }
+ return instances;
+}
+
#if ENABLE_THUMBNAIL_GENERATOR
void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb)
#else
@@ -1117,7 +1153,7 @@ void GCode::_do_export(Print& print, FILE* file)
for (auto layer : object->support_layers())
zs.push_back(layer->print_z);
std::sort(zs.begin(), zs.end());
- m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin()));
+ m_layer_count += (unsigned int)(object->instances().size() * (std::unique(zs.begin(), zs.end()) - zs.begin()));
}
} else {
// Print all objects with the same print_z together.
@@ -1210,13 +1246,18 @@ void GCode::_do_export(Print& print, FILE* file)
ToolOrdering tool_ordering;
unsigned int initial_extruder_id = (unsigned int)-1;
unsigned int final_extruder_id = (unsigned int)-1;
- size_t initial_print_object_id = 0;
bool has_wipe_tower = false;
+ std::vector print_object_instances_ordering;
+ std::vector::const_iterator print_object_instance_sequential_active;
if (print.config().complete_objects.value) {
+ // Order object instances for sequential print.
+ print_object_instances_ordering = sort_object_instances_by_model_order(print);
+// print_object_instances_ordering = sort_object_instances_by_max_z(print);
// Find the 1st printing object, find its tool ordering and the initial extruder ID.
- for (; initial_print_object_id < print.objects().size(); ++initial_print_object_id) {
- tool_ordering = ToolOrdering(*print.objects()[initial_print_object_id], initial_extruder_id);
- if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
+ print_object_instance_sequential_active = print_object_instances_ordering.begin();
+ for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
+ tool_ordering = ToolOrdering(*(*print_object_instance_sequential_active)->print_object, initial_extruder_id);
+ if ((initial_extruder_id = tool_ordering.first_extruder()) != static_cast(-1))
break;
}
// We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode.
@@ -1236,6 +1277,8 @@ void GCode::_do_export(Print& print, FILE* file)
// In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z.
// Therefore initialize the printing extruders from there.
this->set_extruders(tool_ordering.all_extruders());
+ // Order object instances using a nearest neighbor search.
+ print_object_instances_ordering = chain_print_object_instances(print);
}
if (initial_extruder_id == (unsigned int)-1) {
// Nothing to print!
@@ -1316,72 +1359,64 @@ void GCode::_do_export(Print& print, FILE* file)
// Do all objects for each layer.
if (print.config().complete_objects.value) {
- // Print objects from the smallest to the tallest to avoid collisions
- // when moving onto next object starting point.
- std::vector objects(print.objects());
- std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size(2) < po2->size(2); });
size_t finished_objects = 0;
- for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) {
- const PrintObject &object = *objects[object_id];
- for (const Point © : object.copies()) {
- // Get optimal tool ordering to minimize tool switches of a multi-exruder print.
- if (object_id != initial_print_object_id || © != object.copies().data()) {
- // Don't initialize for the first object and first copy.
- tool_ordering = ToolOrdering(object, final_extruder_id);
- unsigned int new_extruder_id = tool_ordering.first_extruder();
- if (new_extruder_id == (unsigned int)-1)
- // Skip this object.
- continue;
- initial_extruder_id = new_extruder_id;
- final_extruder_id = tool_ordering.last_extruder();
- assert(final_extruder_id != (unsigned int)-1);
- }
- print.throw_if_canceled();
- this->set_origin(unscale(copy));
- if (finished_objects > 0) {
- // Move to the origin position for the copy we're going to print.
- // This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
- m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer
- m_avoid_crossing_perimeters.use_external_mp_once = true;
- _write(file, this->retract());
- _write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
- m_enable_cooling_markers = true;
- // Disable motion planner when traveling to first object point.
- m_avoid_crossing_perimeters.disable_once = true;
- // Ff we are printing the bottom layer of an object, and we have already finished
- // another one, set first layer temperatures. This happens before the Z move
- // is triggered, so machine has more time to reach such temperatures.
- m_placeholder_parser.set("current_object_idx", int(finished_objects));
- std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id);
- // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
- this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
- this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
- _writeln(file, between_objects_gcode);
- }
- // Reset the cooling buffer internal state (the current position, feed rate, accelerations).
- m_cooling_buffer->reset();
- m_cooling_buffer->set_current_extruder(initial_extruder_id);
- // Pair the object layers with the support layers by z, extrude them.
- std::vector layers_to_print = collect_layers_to_print(object);
- for (const LayerToPrint <p : layers_to_print) {
- std::vector lrs;
- lrs.emplace_back(std::move(ltp));
- this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), nullptr, © - object.copies().data());
- print.throw_if_canceled();
- }
-#ifdef HAS_PRESSURE_EQUALIZER
- if (m_pressure_equalizer)
- _write(file, m_pressure_equalizer->process("", true));
-#endif /* HAS_PRESSURE_EQUALIZER */
- ++ finished_objects;
- // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
- // Reset it when starting another object from 1st layer.
- m_second_layer_things_done = false;
+ const PrintObject *prev_object = (*print_object_instance_sequential_active)->print_object;
+ for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
+ const PrintObject &object = *(*print_object_instance_sequential_active)->print_object;
+ if (&object != prev_object || tool_ordering.first_extruder() != final_extruder_id) {
+ tool_ordering = ToolOrdering(object, final_extruder_id);
+ unsigned int new_extruder_id = tool_ordering.first_extruder();
+ if (new_extruder_id == (unsigned int)-1)
+ // Skip this object.
+ continue;
+ initial_extruder_id = new_extruder_id;
+ final_extruder_id = tool_ordering.last_extruder();
+ assert(final_extruder_id != (unsigned int)-1);
}
+ print.throw_if_canceled();
+ this->set_origin(unscale((*print_object_instance_sequential_active)->shift));
+ if (finished_objects > 0) {
+ // Move to the origin position for the copy we're going to print.
+ // This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
+ m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer
+ m_avoid_crossing_perimeters.use_external_mp_once = true;
+ _write(file, this->retract());
+ _write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
+ m_enable_cooling_markers = true;
+ // Disable motion planner when traveling to first object point.
+ m_avoid_crossing_perimeters.disable_once = true;
+ // Ff we are printing the bottom layer of an object, and we have already finished
+ // another one, set first layer temperatures. This happens before the Z move
+ // is triggered, so machine has more time to reach such temperatures.
+ m_placeholder_parser.set("current_object_idx", int(finished_objects));
+ std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id);
+ // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
+ this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
+ this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
+ _writeln(file, between_objects_gcode);
+ }
+ // Reset the cooling buffer internal state (the current position, feed rate, accelerations).
+ m_cooling_buffer->reset();
+ m_cooling_buffer->set_current_extruder(initial_extruder_id);
+ // Pair the object layers with the support layers by z, extrude them.
+ std::vector layers_to_print = collect_layers_to_print(object);
+ for (const LayerToPrint <p : layers_to_print) {
+ std::vector lrs;
+ lrs.emplace_back(std::move(ltp));
+ this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), nullptr, *print_object_instance_sequential_active - object.instances().data());
+ print.throw_if_canceled();
+ }
+#ifdef HAS_PRESSURE_EQUALIZER
+ if (m_pressure_equalizer)
+ _write(file, m_pressure_equalizer->process("", true));
+#endif /* HAS_PRESSURE_EQUALIZER */
+ ++ finished_objects;
+ // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
+ // Reset it when starting another object from 1st layer.
+ m_second_layer_things_done = false;
+ prev_object = &object;
}
} else {
- // Order object instances using a nearest neighbor search.
- std::vector> print_object_instances_ordering = chain_print_object_instances(print);
// Sort layers by Z.
// All extrusion moves with the same top layer height are extruded uninterrupted.
std::vector>> layers_to_print = collect_layers_to_print(print);
@@ -1683,12 +1718,12 @@ inline std::vector& object_islands_by_extruder(
}
std::vector GCode::sort_print_object_instances(
- std::vector &objects_by_extruder,
- const std::vector &layers,
+ std::vector &objects_by_extruder,
+ const std::vector &layers,
// Ordering must be defined for normal (non-sequential print).
- const std::vector> *ordering,
+ const std::vector *ordering,
// For sequential print, the instance of the object to be printing has to be defined.
- const size_t single_object_instance_idx)
+ const size_t single_object_instance_idx)
{
std::vector out;
@@ -1715,13 +1750,13 @@ std::vector GCode::sort_print_object_instances(
if (! sorted.empty()) {
const Print &print = *sorted.front().first->print();
out.reserve(sorted.size());
- for (const std::pair &instance_id : *ordering) {
- const PrintObject &print_object = *print.objects()[instance_id.first];
+ for (const PrintInstance *instance : *ordering) {
+ const PrintObject &print_object = *instance->print_object;
std::pair key(&print_object, nullptr);
auto it = std::lower_bound(sorted.begin(), sorted.end(), key);
if (it != sorted.end() && it->first == &print_object)
// ObjectByExtruder for this PrintObject was found.
- out.emplace_back(*it->second, it->second - objects_by_extruder.data(), print_object, instance_id.second);
+ out.emplace_back(*it->second, it->second - objects_by_extruder.data(), print_object, instance - print_object.instances().data());
}
}
}
@@ -1732,7 +1767,7 @@ namespace ProcessLayer
{
static std::string emit_custom_gcode_per_print_z(
- const Model::CustomGCode *custom_gcode,
+ const CustomGCode::Item *custom_gcode,
// ID of the first extruder printing this layer.
unsigned int first_extruder_id,
bool single_material_print)
@@ -1865,16 +1900,16 @@ namespace Skirt {
// and performing the extruder specific extrusions together.
void GCode::process_layer(
// Write into the output file.
- FILE *file,
- const Print &print,
+ FILE *file,
+ const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z.
- const std::vector &layers,
- const LayerTools &layer_tools,
+ const std::vector &layers,
+ const LayerTools &layer_tools,
// Pairs of PrintObject index and its instance index.
- const std::vector> *ordering,
+ const std::vector *ordering,
// If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object.
- const size_t single_object_instance_idx)
+ const size_t single_object_instance_idx)
{
assert(! layers.empty());
// assert(! layer_tools.extruders.empty());
@@ -2083,7 +2118,7 @@ void GCode::process_layer(
// by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools)
correct_extruder_id = layer_tools.extruders.back();
}
- entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->copies().size());
+ entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->instances().size());
if (entity_overrides == nullptr) {
printing_extruders.emplace_back(correct_extruder_id);
} else {
@@ -2197,7 +2232,7 @@ void GCode::process_layer(
if (this->config().gcode_label_objects)
gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n";
// When starting a new object, use the external motion planner for the first travel move.
- const Point &offset = instance_to_print.print_object.copies()[instance_to_print.instance_id];
+ const Point &offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
std::pair this_object_copy(&instance_to_print.print_object, offset);
if (m_last_obj_copy != this_object_copy)
m_avoid_crossing_perimeters.use_external_mp_once = true;
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index 0344924a1..10463277b 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -226,7 +226,7 @@ private:
const std::vector &layers,
const LayerTools &layer_tools,
// Pairs of PrintObject index and its instance index.
- const std::vector> *ordering,
+ const std::vector *ordering,
// If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object.
const size_t single_object_idx = size_t(-1));
@@ -300,7 +300,7 @@ private:
// Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances.
const std::vector &layers,
// Ordering must be defined for normal (non-sequential print).
- const std::vector> *ordering,
+ const std::vector *ordering,
// For sequential print, the instance of the object to be printing has to be defined.
const size_t single_object_instance_idx);
diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp
index 1fedcf3f0..7a8271e30 100644
--- a/src/libslic3r/GCode/PrintExtents.cpp
+++ b/src/libslic3r/GCode/PrintExtents.cpp
@@ -121,9 +121,9 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object
if (support_layer)
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
bbox_this.merge(extrusionentity_extents(extrusion_entity));
- for (const Point &offset : print_object.copies()) {
+ for (const PrintInstance &instance : print_object.instances()) {
BoundingBoxf bbox_translated(bbox_this);
- bbox_translated.translate(unscale(offset));
+ bbox_translated.translate(unscale(instance.shift));
bbox.merge(bbox_translated);
}
}
diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp
index 3e01e2594..f5ff33e41 100644
--- a/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/src/libslic3r/GCode/ToolOrdering.cpp
@@ -133,7 +133,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
num_extruders > 1 && print.object_extruders().size() == 1) {
// Printing a single extruder platter on a printer with more than 1 extruder (or single-extruder multi-material).
// There may be custom per-layer tool changes available at the model.
- per_layer_extruder_switches = custom_tool_changes(print.model(), num_extruders);
+ per_layer_extruder_switches = custom_tool_changes(print.model().custom_gcode_per_print_z, num_extruders);
}
// Collect extruders reuqired to print the layers.
@@ -462,7 +462,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
// Only valid for non-sequential print.
assert(! print.config().complete_objects.value);
- const Model::CustomGCodeInfo &custom_gcode_per_print_z = print.model().custom_gcode_per_print_z;
+ const CustomGCode::Info &custom_gcode_per_print_z = print.model().custom_gcode_per_print_z;
if (custom_gcode_per_print_z.gcodes.empty())
return;
@@ -483,7 +483,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
// Custom G-codes were processed.
break;
// Some custom G-code is configured for this layer or a layer below.
- const Model::CustomGCode &custom_gcode = *custom_gcode_it;
+ const CustomGCode::Item &custom_gcode = *custom_gcode_it;
// print_z of the layer below the current layer.
coordf_t print_z_below = 0.;
if (auto it_lt_below = it_lt; ++ it_lt_below != m_layer_tools.rend())
@@ -491,7 +491,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
if (custom_gcode.print_z > print_z_below + 0.5 * EPSILON) {
// The custom G-code applies to the current layer.
if ( tool_changes_as_color_changes || custom_gcode.gcode != ColorChangeCode ||
- (custom_gcode.extruder <= num_extruders && extruder_printing_above[unsigned(custom_gcode.extruder - 1)]))
+ (custom_gcode.extruder <= int(num_extruders) && extruder_printing_above[unsigned(custom_gcode.extruder - 1)]))
// If it is color change, it will actually be useful as the exturder above will print.
lt.custom_gcode = &custom_gcode;
// Consume that custom G-code event.
@@ -602,7 +602,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON);
if (this_layer == nullptr)
continue;
- size_t num_of_copies = object->copies().size();
+ size_t num_of_copies = object->instances().size();
// iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves
for (unsigned int copy = 0; copy < num_of_copies; ++copy) {
@@ -677,7 +677,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON);
if (this_layer == nullptr)
continue;
- size_t num_of_copies = object->copies().size();
+ size_t num_of_copies = object->instances().size();
for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp
index 08dd72656..de0460f1b 100644
--- a/src/libslic3r/GCode/ToolOrdering.hpp
+++ b/src/libslic3r/GCode/ToolOrdering.hpp
@@ -114,7 +114,7 @@ public:
size_t wipe_tower_partitions = 0;
coordf_t wipe_tower_layer_height = 0.;
// Custom G-code (color change, extruder switch, pause) to be performed before this layer starts to print.
- const Model::CustomGCode *custom_gcode = nullptr;
+ const CustomGCode::Item *custom_gcode = nullptr;
WipingExtrusions& wiping_extrusions() {
m_wiping_extrusions.set_layer_tools_ptr(this);
diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp
index 4c53048dc..38a1c3ebe 100644
--- a/src/libslic3r/GCodeWriter.cpp
+++ b/src/libslic3r/GCodeWriter.cpp
@@ -1,4 +1,5 @@
#include "GCodeWriter.hpp"
+#include "CustomGCode.hpp"
#include
#include
#include
diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp
index abeaf0024..3a57c8bd2 100644
--- a/src/libslic3r/GCodeWriter.hpp
+++ b/src/libslic3r/GCodeWriter.hpp
@@ -10,11 +10,6 @@
namespace Slic3r {
-// Additional Codes which can be set by user using DoubleSlider
-static constexpr char ColorChangeCode[] = "M600";
-static constexpr char PausePrintCode[] = "M601";
-static constexpr char ToolChangeCode[] = "tool_change";
-
class GCodeWriter {
public:
GCodeConfig config;
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index 0513653a2..c5c457e8f 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -126,7 +126,8 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
if (add_default_instances)
model.add_default_instances();
- update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z.gcodes, config);
+ CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
+ CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
return model;
}
@@ -163,7 +164,8 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
if (add_default_instances)
model.add_default_instances();
- update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z.gcodes, config);
+ CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
+ CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
return model;
}
@@ -1848,19 +1850,6 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
return ret;
}
-// Return pairs of sorted by increasing print_z from custom_gcode_per_print_z.
-// print_z corresponds to the first layer printed with the new extruder.
-std::vector> custom_tool_changes(const Model &model, size_t num_extruders)
-{
- std::vector> custom_tool_changes;
- for (const Model::CustomGCode &custom_gcode : model.custom_gcode_per_print_z.gcodes)
- if (custom_gcode.gcode == ToolChangeCode) {
- // If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
- custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder));
- }
- return custom_tool_changes;
-}
-
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing.
bool model_object_list_equal(const Model &model_old, const Model &model_new)
@@ -1949,28 +1938,6 @@ extern bool model_has_advanced_features(const Model &model)
return false;
}
-extern void update_custom_gcode_per_print_z_from_config(std::vector& custom_gcode_per_print_z, DynamicPrintConfig* config)
-{
- auto *colorprint_heights = config->option("colorprint_heights");
- if (colorprint_heights == nullptr)
- return;
-
- if (custom_gcode_per_print_z.empty() && ! colorprint_heights->values.empty()) {
- // Convert the old colorprint_heighs only if there is no equivalent data in a new format.
- const std::vector& colors = GCodePreviewData::ColorPrintColors();
- const auto& colorprint_values = colorprint_heights->values;
- custom_gcode_per_print_z.clear();
- custom_gcode_per_print_z.reserve(colorprint_values.size());
- int i = 0;
- for (auto val : colorprint_values)
- custom_gcode_per_print_z.emplace_back(Model::CustomGCode{ val, ColorChangeCode, 1, colors[(++i)%7] });
- }
-
- // The "colorprint_heights" config value has been deprecated. At this point of time it has been converted
- // to a new format and therefore it shall be erased.
- config->erase("colorprint_heights");
-}
-
#ifndef NDEBUG
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
void check_model_ids_validity(const Model &model)
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index 5f07acc6a..e0859e81d 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -12,6 +12,7 @@
#include "SLA/Hollowing.hpp"
#include "TriangleMesh.hpp"
#include "Arrange.hpp"
+#include "CustomGCode.hpp"
#include