Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorenricoturri1966 <enricoturri@seznam.cz>2021-09-09 12:01:49 +0300
committerenricoturri1966 <enricoturri@seznam.cz>2021-09-09 12:01:49 +0300
commit3369e1b599c66d6abe86ce316cecdc38732d7017 (patch)
tree5139e55968f5be054333a1c60551d711a2092001 /src/libslic3r
parentf908197a77ed3eac9c43a96bb2d588fc5ecf2040 (diff)
parent3e0b7910eab5085c9903464f0b2af93bfe3e7d3c (diff)
Fixed conflicts after merge with master
Diffstat (limited to 'src/libslic3r')
-rw-r--r--src/libslic3r/BlacklistedLibraryCheck.cpp13
-rw-r--r--src/libslic3r/Brim.cpp40
-rw-r--r--src/libslic3r/Format/3mf.cpp74
-rw-r--r--src/libslic3r/GCode.cpp193
-rw-r--r--src/libslic3r/GCode.hpp60
-rw-r--r--src/libslic3r/GCode/GCodeProcessor.cpp482
-rw-r--r--src/libslic3r/GCode/GCodeProcessor.hpp62
-rw-r--r--src/libslic3r/GCodeReader.cpp45
-rw-r--r--src/libslic3r/GCodeReader.hpp31
-rw-r--r--src/libslic3r/GCodeWriter.cpp160
-rw-r--r--src/libslic3r/Model.cpp27
-rw-r--r--src/libslic3r/Model.hpp13
-rw-r--r--src/libslic3r/Polyline.cpp4
-rw-r--r--src/libslic3r/Polyline.hpp2
-rw-r--r--src/libslic3r/Preset.cpp6
-rw-r--r--src/libslic3r/PresetBundle.cpp31
-rw-r--r--src/libslic3r/Print.cpp2
-rw-r--r--src/libslic3r/PrintConfig.cpp25
-rw-r--r--src/libslic3r/PrintConfig.hpp4
-rw-r--r--src/libslic3r/PrintObject.cpp28
-rw-r--r--src/libslic3r/QuadricEdgeCollapse.cpp162
-rw-r--r--src/libslic3r/SupportMaterial.cpp14
-rw-r--r--src/libslic3r/Technologies.hpp31
-rw-r--r--src/libslic3r/utils.cpp15
24 files changed, 1094 insertions, 430 deletions
diff --git a/src/libslic3r/BlacklistedLibraryCheck.cpp b/src/libslic3r/BlacklistedLibraryCheck.cpp
index 3d2ee3482..76f675c70 100644
--- a/src/libslic3r/BlacklistedLibraryCheck.cpp
+++ b/src/libslic3r/BlacklistedLibraryCheck.cpp
@@ -12,7 +12,7 @@ namespace Slic3r {
#ifdef WIN32
//only dll name with .dll suffix - currently case sensitive
-const std::vector<std::wstring> BlacklistedLibraryCheck::blacklist({ L"NahimicOSD.dll" });
+const std::vector<std::wstring> BlacklistedLibraryCheck::blacklist({ L"NahimicOSD.dll", L"SS2OSD.dll" });
bool BlacklistedLibraryCheck::get_blacklisted(std::vector<std::wstring>& names)
{
@@ -33,22 +33,20 @@ std::wstring BlacklistedLibraryCheck::get_blacklisted_string()
bool BlacklistedLibraryCheck::perform_check()
{
- // Get a handle to the process.
- HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId());
- if (NULL == hProcess)
- return false;
+ // Get the pseudo-handle for the current process.
+ HANDLE hCurrentProcess = GetCurrentProcess();
// Get a list of all the modules in this process.
HMODULE hMods[1024];
DWORD cbNeeded;
- if (EnumProcessModulesEx(hProcess, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL))
+ if (EnumProcessModulesEx(hCurrentProcess, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL))
{
//printf("Total Dlls: %d\n", cbNeeded / sizeof(HMODULE));
for (unsigned int i = 0; i < cbNeeded / sizeof(HMODULE); ++ i)
{
wchar_t szModName[MAX_PATH];
// Get the full path to the module's file.
- if (GetModuleFileNameExW(hProcess, hMods[i], szModName, MAX_PATH))
+ if (GetModuleFileNameExW(hCurrentProcess, hMods[i], szModName, MAX_PATH))
{
// Add to list if blacklisted
if (BlacklistedLibraryCheck::is_blacklisted(szModName)) {
@@ -61,7 +59,6 @@ bool BlacklistedLibraryCheck::perform_check()
}
}
- CloseHandle(hProcess);
//printf("\n");
return !m_found.empty();
}
diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp
index f4455fdd5..db31975e3 100644
--- a/src/libslic3r/Brim.cpp
+++ b/src/libslic3r/Brim.cpp
@@ -134,10 +134,10 @@ static Polygons top_level_outer_brim_islands(const ConstPrintObjectPtrs &top_lev
Polygons islands;
for (const PrintObject *object : top_level_objects_with_brim) {
//FIXME how about the brim type?
- auto brim_offset = float(scale_(object->config().brim_offset.value));
+ auto brim_separation = float(scale_(object->config().brim_separation.value));
Polygons islands_object;
for (const ExPolygon &ex_poly : get_print_object_bottom_layer_expolygons(*object)) {
- Polygons contour_offset = offset(ex_poly.contour, brim_offset);
+ Polygons contour_offset = offset(ex_poly.contour, brim_separation, ClipperLib::jtSquare);
for (Polygon &poly : contour_offset)
poly.douglas_peucker(SCALED_RESOLUTION);
@@ -166,7 +166,7 @@ static ExPolygons top_level_outer_brim_area(const Print &print
for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
const PrintObject *object = print.objects()[print_object_idx];
const BrimType brim_type = object->config().brim_type.value;
- const float brim_offset = scale_(object->config().brim_offset.value);
+ const float brim_separation = scale_(object->config().brim_separation.value);
const float brim_width = scale_(object->config().brim_width.value);
const bool is_top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end();
@@ -174,16 +174,16 @@ static ExPolygons top_level_outer_brim_area(const Print &print
ExPolygons no_brim_area_object;
for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx]) {
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) && is_top_outer_brim)
- append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_offset), offset(ex_poly.contour, brim_offset)));
+ append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_separation, ClipperLib::jtSquare), offset(ex_poly.contour, brim_separation, ClipperLib::jtSquare)));
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
- append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset));
+ append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset, ClipperLib::jtSquare));
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
- append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly.holes));
+ append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset, ClipperLib::jtSquare), ex_poly.holes));
if (brim_type != BrimType::btNoBrim)
- append(no_brim_area_object, offset_ex(ExPolygon(ex_poly.contour), brim_offset));
+ append(no_brim_area_object, offset_ex(ExPolygon(ex_poly.contour), brim_separation, ClipperLib::jtSquare));
no_brim_area_object.emplace_back(ex_poly.contour);
}
@@ -212,11 +212,11 @@ static ExPolygons inner_brim_area(const Print &print,
ExPolygons no_brim_area;
Polygons holes;
for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
- const PrintObject *object = print.objects()[print_object_idx];
- const BrimType brim_type = object->config().brim_type.value;
- const float brim_offset = scale_(object->config().brim_offset.value);
- const float brim_width = scale_(object->config().brim_width.value);
- const bool top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end();
+ const PrintObject *object = print.objects()[print_object_idx];
+ const BrimType brim_type = object->config().brim_type.value;
+ const float brim_separation = scale_(object->config().brim_separation.value);
+ const float brim_width = scale_(object->config().brim_width.value);
+ const bool top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end();
ExPolygons brim_area_object;
ExPolygons no_brim_area_object;
@@ -226,21 +226,21 @@ static ExPolygons inner_brim_area(const Print &print,
if (top_outer_brim)
no_brim_area_object.emplace_back(ex_poly);
else
- append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_offset), offset(ex_poly.contour, brim_offset)));
+ append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_separation, ClipperLib::jtSquare), offset(ex_poly.contour, brim_separation, ClipperLib::jtSquare)));
}
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner)
- append(brim_area_object, diff_ex(offset_ex(ex_poly.holes, -brim_offset), offset_ex(ex_poly.holes, -brim_width - brim_offset)));
+ append(brim_area_object, diff_ex(offset_ex(ex_poly.holes, -brim_separation, ClipperLib::jtSquare), offset_ex(ex_poly.holes, -brim_width - brim_separation, ClipperLib::jtSquare)));
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
- append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly.holes));
+ append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset, ClipperLib::jtSquare), ex_poly.holes));
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
- append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset));
+ append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset, ClipperLib::jtSquare));
append(holes_object, ex_poly.holes);
}
- append(no_brim_area_object, offset_ex(bottom_layers_expolygons[print_object_idx], brim_offset));
+ append(no_brim_area_object, offset_ex(bottom_layers_expolygons[print_object_idx], brim_separation, ClipperLib::jtSquare));
for (const PrintInstance &instance : object->instances()) {
append_and_translate(brim_area, brim_area_object, instance);
@@ -356,12 +356,12 @@ static void make_inner_brim(const Print &print,
Flow flow = print.brim_flow();
ExPolygons islands_ex = inner_brim_area(print, top_level_objects_with_brim, bottom_layers_expolygons, float(flow.scaled_spacing()));
Polygons loops;
- islands_ex = offset_ex(islands_ex, -0.5f * float(flow.scaled_spacing()), jtSquare);
+ islands_ex = offset_ex(islands_ex, -0.5f * float(flow.scaled_spacing()), ClipperLib::jtSquare);
for (size_t i = 0; !islands_ex.empty(); ++i) {
for (ExPolygon &poly_ex : islands_ex)
poly_ex.douglas_peucker(SCALED_RESOLUTION);
polygons_append(loops, to_polygons(islands_ex));
- islands_ex = offset_ex(islands_ex, -float(flow.scaled_spacing()), jtSquare);
+ islands_ex = offset_ex(islands_ex, -float(flow.scaled_spacing()), ClipperLib::jtSquare);
}
loops = union_pt_chained_outside_in(loops);
@@ -385,7 +385,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
size_t num_loops = size_t(floor(max_brim_width(print.objects()) / flow.spacing()));
for (size_t i = 0; i < num_loops; ++i) {
try_cancel();
- islands = offset(islands, float(flow.scaled_spacing()), jtSquare);
+ islands = offset(islands, float(flow.scaled_spacing()), ClipperLib::jtSquare);
for (Polygon &poly : islands)
poly.douglas_peucker(SCALED_RESOLUTION);
polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing())));
diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index 57b0d8115..295c1413d 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -49,6 +49,17 @@ const unsigned int VERSION_3MF = 1;
const unsigned int VERSION_3MF_COMPATIBLE = 2;
const char* SLIC3RPE_3MF_VERSION = "slic3rpe:Version3mf"; // definition of the metadata name saved into .model file
+// Painting gizmos data version numbers
+// 0 : 3MF files saved by older PrusaSlicer or the painting gizmo wasn't used. No version definition in them.
+// 1 : Introduction of painting gizmos data versioning. No other changes in painting gizmos data.
+const unsigned int FDM_SUPPORTS_PAINTING_VERSION = 1;
+const unsigned int SEAM_PAINTING_VERSION = 1;
+const unsigned int MM_PAINTING_VERSION = 1;
+
+const std::string SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION = "slic3rpe:FdmSupportsPaintingVersion";
+const std::string SLIC3RPE_SEAM_PAINTING_VERSION = "slic3rpe:SeamPaintingVersion";
+const std::string SLIC3RPE_MM_PAINTING_VERSION = "slic3rpe:MmPaintingVersion";
+
const std::string MODEL_FOLDER = "3D/";
const std::string MODEL_EXTENSION = ".model";
const std::string MODEL_FILE = "3D/3dmodel.model"; // << this is the only format of the string which works with CURA
@@ -393,6 +404,10 @@ namespace Slic3r {
unsigned int m_version;
bool m_check_version;
+ unsigned int m_fdm_supports_painting_version = 0;
+ unsigned int m_seam_painting_version = 0;
+ unsigned int m_mm_painting_version = 0;
+
XML_Parser m_xml_parser;
// Error code returned by the application side of the parser. In that case the expat may not reliably deliver the error state
// after returning from XML_Parse() function, thus we keep the error state here.
@@ -420,6 +435,7 @@ namespace Slic3r {
~_3MF_Importer();
bool load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, bool check_version);
+ unsigned int version() const { return m_version; }
private:
void _destroy_xml_parser();
@@ -542,6 +558,9 @@ namespace Slic3r {
bool _3MF_Importer::load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, bool check_version)
{
m_version = 0;
+ m_fdm_supports_painting_version = 0;
+ m_seam_painting_version = 0;
+ m_mm_painting_version = 0;
m_check_version = check_version;
m_model = &model;
m_unit_factor = 1.0f;
@@ -1668,6 +1687,12 @@ namespace Slic3r {
return true;
}
+ inline static void check_painting_version(unsigned int loaded_version, unsigned int highest_supported_version, const std::string &error_msg)
+ {
+ if (loaded_version > highest_supported_version)
+ throw version_error(error_msg);
+ }
+
bool _3MF_Importer::_handle_end_metadata()
{
if (m_curr_metadata_name == SLIC3RPE_3MF_VERSION) {
@@ -1680,6 +1705,24 @@ namespace Slic3r {
}
}
+ if (m_curr_metadata_name == SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION) {
+ m_fdm_supports_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
+ check_painting_version(m_fdm_supports_painting_version, FDM_SUPPORTS_PAINTING_VERSION,
+ _(L("The selected 3MF contains FDM supports painted object using a newer version of PrusaSlicer and is not compatible.")));
+ }
+
+ if (m_curr_metadata_name == SLIC3RPE_SEAM_PAINTING_VERSION) {
+ m_seam_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
+ check_painting_version(m_seam_painting_version, SEAM_PAINTING_VERSION,
+ _(L("The selected 3MF contains seam painted object using a newer version of PrusaSlicer and is not compatible.")));
+ }
+
+ if (m_curr_metadata_name == SLIC3RPE_MM_PAINTING_VERSION) {
+ m_mm_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
+ check_painting_version(m_mm_painting_version, MM_PAINTING_VERSION,
+ _(L("The selected 3MF contains multi-material painted object using a newer version of PrusaSlicer and is not compatible.")));
+ }
+
return true;
}
@@ -2293,6 +2336,16 @@ namespace Slic3r {
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n";
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "</" << METADATA_TAG << ">\n";
+
+ if (model.is_fdm_support_painted())
+ stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION << "\">" << FDM_SUPPORTS_PAINTING_VERSION << "</" << METADATA_TAG << ">\n";
+
+ if (model.is_seam_painted())
+ stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_SEAM_PAINTING_VERSION << "\">" << SEAM_PAINTING_VERSION << "</" << METADATA_TAG << ">\n";
+
+ if (model.is_mm_painted())
+ stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_MM_PAINTING_VERSION << "\">" << MM_PAINTING_VERSION << "</" << METADATA_TAG << ">\n";
+
std::string name = xml_escape(boost::filesystem::path(filename).stem().string());
stream << " <" << METADATA_TAG << " name=\"Title\">" << name << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"Designer\">" << "</" << METADATA_TAG << ">\n";
@@ -2613,9 +2666,16 @@ namespace Slic3r {
bool _3MF_Exporter::_add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items)
{
+#if ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
+ // This happens for empty projects
+#endif // ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
if (build_items.size() == 0) {
add_error("No build item found");
+#if ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
+ return true;
+#else
return false;
+#endif // ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
}
stream << " <" << BUILD_TAG << ">\n";
@@ -2990,6 +3050,19 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
return true;
}
+// Perform conversions based on the config values available.
+//FIXME provide a version of PrusaSlicer that stored the project file (3MF).
+static void handle_legacy_project_loaded(unsigned int version_project_file, DynamicPrintConfig& config)
+{
+ if (! config.has("brim_separation")) {
+ if (auto *opt_elephant_foot = config.option<ConfigOptionFloat>("elefant_foot_compensation", false); opt_elephant_foot) {
+ // Conversion from older PrusaSlicer which applied brim separation equal to elephant foot compensation.
+ auto *opt_brim_separation = config.option<ConfigOptionFloat>("brim_separation", true);
+ opt_brim_separation->value = opt_elephant_foot->value;
+ }
+ }
+}
+
bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Model* model, bool check_version)
{
if (path == nullptr || model == nullptr)
@@ -3000,6 +3073,7 @@ bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionCo
_3MF_Importer importer;
bool res = importer.load_model_from_file(path, *model, config, config_substitutions, check_version);
importer.log_errors();
+ handle_legacy_project_loaded(importer.version(), config);
return res;
}
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 85e6f810b..efea240e5 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -744,27 +744,28 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
std::string path_tmp(path);
path_tmp += ".tmp";
- FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb");
- if (file == nullptr)
+ m_processor.initialize(path_tmp);
+ GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor);
+ if (! file.is_open())
throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
try {
m_placeholder_parser_failed_templates.clear();
this->_do_export(*print, file, thumbnail_cb);
- fflush(file);
- if (ferror(file)) {
- fclose(file);
+ file.flush();
+ if (file.is_error()) {
+ file.close();
boost::nowide::remove(path_tmp.c_str());
throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
}
} catch (std::exception & /* ex */) {
// Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown.
// Close and remove the file.
- fclose(file);
+ file.close();
boost::nowide::remove(path_tmp.c_str());
throw;
}
- fclose(file);
+ file.close();
if (! m_placeholder_parser_failed_templates.empty()) {
// G-code export proceeded, but some of the PlaceholderParser substitutions failed.
@@ -782,7 +783,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
}
BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info();
- m_processor.process_file(path_tmp, true, [print]() { print->throw_if_canceled(); });
+ m_processor.finalize();
// DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics);
DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics);
if (result != nullptr) {
@@ -1046,7 +1047,7 @@ std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Pri
return instances;
}
-void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb)
+void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb)
{
PROFILE_FUNC();
@@ -1111,10 +1112,10 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
#endif /* HAS_PRESSURE_EQUALIZER */
// Write information on the generator.
- _write_format(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str());
+ file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str());
DoExport::export_thumbnails_to_file(thumbnail_cb, print.full_print_config().option<ConfigOptionPoints>("thumbnails")->values,
- [this, file](const char* sz) { this->_write(file, sz); },
+ [&file](const char* sz) { file.write(sz); },
[&print]() { print.throw_if_canceled(); });
// Write notes (content of the Print Settings tab -> Notes)
@@ -1125,10 +1126,10 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
// Remove the trailing '\r' from the '\r\n' sequence.
if (! line.empty() && line.back() == '\r')
line.pop_back();
- _write_format(file, "; %s\n", line.c_str());
+ file.write_format("; %s\n", line.c_str());
}
if (! lines.empty())
- _write(file, "\n");
+ file.write("\n");
}
print.throw_if_canceled();
@@ -1139,22 +1140,22 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
const double first_layer_height = print.config().first_layer_height.value;
for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) {
const PrintRegion &region = print.get_print_region(region_id);
- _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width());
- _write_format(file, "; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width());
- _write_format(file, "; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width());
- _write_format(file, "; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width());
- _write_format(file, "; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width());
+ file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width());
+ file.write_format("; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width());
+ file.write_format("; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width());
+ file.write_format("; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width());
+ file.write_format("; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width());
if (print.has_support_material())
- _write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
+ file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
if (print.config().first_layer_extrusion_width.value > 0)
- _write_format(file, "; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width());
- _write_format(file, "\n");
+ file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width());
+ file.write_format("\n");
}
print.throw_if_canceled();
// adds tags for time estimators
if (print.config().remaining_times.value)
- _write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::First_Line_M73_Placeholder).c_str());
+ file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::First_Line_M73_Placeholder).c_str());
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
m_placeholder_parser = print.placeholder_parser();
@@ -1218,7 +1219,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
// Disable fan.
if (! print.config().cooling.get_at(initial_extruder_id) || print.config().disable_fan_first_layers.get_at(initial_extruder_id))
- _write(file, m_writer.set_fan(0, true));
+ file.write(m_writer.set_fan(0, true));
// Let the start-up script prime the 1st printing tool.
m_placeholder_parser.set("initial_tool", initial_extruder_id);
@@ -1261,10 +1262,10 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false);
// adds tag for processor
- _write_format(file, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
+ file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
// Write the custom start G-code
- _writeln(file, start_gcode);
+ file.writeln(start_gcode);
// Process filament-specific gcode.
/* if (has_wipe_tower) {
@@ -1272,14 +1273,14 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
} else {
DynamicConfig config;
config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id)));
- _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config));
+ file.writeln(this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config));
}
*/
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
print.throw_if_canceled();
// Set other general things.
- _write(file, this->preamble());
+ file.write(this->preamble());
// Calculate wiping points if needed
DoExport::init_ooze_prevention(print, m_ooze_prevention);
@@ -1291,7 +1292,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming)) {
// Set initial extruder only after custom start G-code.
// Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
- _write(file, this->set_extruder(initial_extruder_id, 0.));
+ file.write(this->set_extruder(initial_extruder_id, 0.));
}
// Do all objects for each layer.
@@ -1317,8 +1318,8 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
// 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();
- _write(file, this->retract());
- _write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
+ file.write(this->retract());
+ file.write(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();
@@ -1330,7 +1331,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
// 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);
+ file.writeln(between_objects_gcode);
}
// Reset the cooling buffer internal state (the current position, feed rate, accelerations).
m_cooling_buffer->reset();
@@ -1346,7 +1347,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
}
#ifdef HAS_PRESSURE_EQUALIZER
if (m_pressure_equalizer)
- _write(file, m_pressure_equalizer->process("", true));
+ file.write(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.
@@ -1361,9 +1362,9 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
// Prusa Multi-Material wipe tower.
if (has_wipe_tower && ! layers_to_print.empty()) {
m_wipe_tower.reset(new WipeTowerIntegration(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()));
- _write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
+ file.write(m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
if (print.config().single_extruder_multi_material_priming) {
- _write(file, m_wipe_tower->prime(*this));
+ file.write(m_wipe_tower->prime(*this));
// Verify, whether the print overaps the priming extrusions.
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
@@ -1375,15 +1376,15 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
bool overlap = bbox_prime.overlap(bbox_print);
if (print.config().gcode_flavor == gcfMarlinLegacy || print.config().gcode_flavor == gcfMarlinFirmware) {
- _write(file, this->retract());
- _write(file, "M300 S800 P500\n"); // Beep for 500ms, tone 800Hz.
+ file.write(this->retract());
+ file.write("M300 S800 P500\n"); // Beep for 500ms, tone 800Hz.
if (overlap) {
// Wait for the user to remove the priming extrusions.
- _write(file, "M1 Remove priming towers and click button.\n");
+ file.write("M1 Remove priming towers and click button.\n");
} else {
// Just wait for a bit to let the user check, that the priming succeeded.
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
- _write(file, "M1 S10\n");
+ file.write("M1 S10\n");
}
} else {
// This is not Marlin, M1 command is probably not supported.
@@ -1410,19 +1411,19 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
}
#ifdef HAS_PRESSURE_EQUALIZER
if (m_pressure_equalizer)
- _write(file, m_pressure_equalizer->process("", true));
+ file.write(m_pressure_equalizer->process("", true));
#endif /* HAS_PRESSURE_EQUALIZER */
if (m_wipe_tower)
// Purge the extruder, pull out the active filament.
- _write(file, m_wipe_tower->finalize(*this));
+ file.write(m_wipe_tower->finalize(*this));
}
// Write end commands to file.
- _write(file, this->retract());
- _write(file, m_writer.set_fan(false));
+ file.write(this->retract());
+ file.write(m_writer.set_fan(false));
// adds tag for processor
- _write_format(file, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
+ file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
// Process filament-specific gcode in extruder order.
{
@@ -1434,48 +1435,48 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
// Process the end_filament_gcode for the active filament only.
int extruder_id = m_writer.extruder()->id();
config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id));
- _writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(extruder_id), extruder_id, &config));
+ file.writeln(this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(extruder_id), extruder_id, &config));
} else {
for (const std::string &end_gcode : print.config().end_filament_gcode.values) {
int extruder_id = (unsigned int)(&end_gcode - &print.config().end_filament_gcode.values.front());
config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id));
- _writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, extruder_id, &config));
+ file.writeln(this->placeholder_parser_process("end_filament_gcode", end_gcode, extruder_id, &config));
}
}
- _writeln(file, this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config));
+ file.writeln(this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config));
}
- _write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
- _write(file, m_writer.postamble());
+ file.write(m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
+ file.write(m_writer.postamble());
// adds tags for time estimators
if (print.config().remaining_times.value)
- _write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Last_Line_M73_Placeholder).c_str());
+ file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Last_Line_M73_Placeholder).c_str());
print.throw_if_canceled();
// Get filament stats.
- _write(file, DoExport::update_print_stats_and_format_filament_stats(
+ file.write(DoExport::update_print_stats_and_format_filament_stats(
// Const inputs
has_wipe_tower, print.wipe_tower_data(),
m_writer.extruders(),
// Modifies
print.m_print_statistics));
- _write(file, "\n");
- _write_format(file, "; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight);
- _write_format(file, "; total filament cost = %.2lf\n", print.m_print_statistics.total_cost);
+ file.write("\n");
+ file.write_format("; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight);
+ file.write_format("; total filament cost = %.2lf\n", print.m_print_statistics.total_cost);
if (print.m_print_statistics.total_toolchanges > 0)
- _write_format(file, "; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges);
- _write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
+ file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges);
+ file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
// Append full config, delimited by two 'phony' configuration keys prusaslicer_config = begin and prusaslicer_config = end.
// The delimiters are structured as configuration key / value pairs to be parsable by older versions of PrusaSlicer G-code viewer.
{
- _write(file, "\n; prusaslicer_config = begin\n");
+ file.write("\n; prusaslicer_config = begin\n");
std::string full_config;
append_full_config(print, full_config);
if (!full_config.empty())
- _write(file, full_config);
- _write(file, "; prusaslicer_config = end\n");
+ file.write(full_config);
+ file.write("; prusaslicer_config = end\n");
}
print.throw_if_canceled();
}
@@ -1565,16 +1566,16 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
// Print the machine envelope G-code for the Marlin firmware based on the "machine_max_xxx" parameters.
// Do not process this piece of G-code by the time estimator, it already knows the values through another sources.
-void GCode::print_machine_envelope(FILE *file, Print &print)
+void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print)
{
if ((print.config().gcode_flavor.value == gcfMarlinLegacy || print.config().gcode_flavor.value == gcfMarlinFirmware)
&& print.config().machine_limits_usage.value == MachineLimitsUsage::EmitToGCode) {
- fprintf(file, "M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n",
+ file.write_format("M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n",
int(print.config().machine_max_acceleration_x.values.front() + 0.5),
int(print.config().machine_max_acceleration_y.values.front() + 0.5),
int(print.config().machine_max_acceleration_z.values.front() + 0.5),
int(print.config().machine_max_acceleration_e.values.front() + 0.5));
- fprintf(file, "M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n",
+ file.write_format("M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n",
int(print.config().machine_max_feedrate_x.values.front() + 0.5),
int(print.config().machine_max_feedrate_y.values.front() + 0.5),
int(print.config().machine_max_feedrate_z.values.front() + 0.5),
@@ -1587,18 +1588,18 @@ void GCode::print_machine_envelope(FILE *file, Print &print)
int travel_acc = print.config().gcode_flavor == gcfMarlinLegacy
? int(print.config().machine_max_acceleration_extruding.values.front() + 0.5)
: int(print.config().machine_max_acceleration_travel.values.front() + 0.5);
- fprintf(file, "M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
+ file.write_format("M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
int(print.config().machine_max_acceleration_extruding.values.front() + 0.5),
int(print.config().machine_max_acceleration_retracting.values.front() + 0.5),
travel_acc);
assert(is_decimal_separator_point());
- fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
+ file.write_format("M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
print.config().machine_max_jerk_x.values.front(),
print.config().machine_max_jerk_y.values.front(),
print.config().machine_max_jerk_z.values.front(),
print.config().machine_max_jerk_e.values.front());
- fprintf(file, "M205 S%d T%d ; sets the minimum extruding and travel feed rate, mm/sec\n",
+ file.write_format("M205 S%d T%d ; sets the minimum extruding and travel feed rate, mm/sec\n",
int(print.config().machine_min_extruding_rate.values.front() + 0.5),
int(print.config().machine_min_travel_rate.values.front() + 0.5));
}
@@ -1608,7 +1609,7 @@ void GCode::print_machine_envelope(FILE *file, Print &print)
// Only do that if the start G-code does not already contain any M-code controlling an extruder temperature.
// M140 - Set Extruder Temperature
// M190 - Set Extruder Temperature and Wait
-void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
+void GCode::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
{
// Initial bed temperature based on the first extruder.
int temp = print.config().first_layer_bed_temperature.get_at(first_printing_extruder_id);
@@ -1621,7 +1622,7 @@ void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const s
// the custom start G-code emited these.
std::string set_temp_gcode = m_writer.set_bed_temperature(temp, wait);
if (! temp_set_by_gcode)
- _write(file, set_temp_gcode);
+ file.write(set_temp_gcode);
}
// Write 1st layer extruder temperatures into the G-code.
@@ -1629,7 +1630,7 @@ void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const s
// M104 - Set Extruder Temperature
// M109 - Set Extruder Temperature and Wait
// RepRapFirmware: G10 Sxx
-void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
+void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
{
// Is the bed temperature set by the provided custom G-code?
int temp_by_gcode = -1;
@@ -1646,7 +1647,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
// Set temperature of the first printing extruder only.
int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id);
if (temp > 0)
- _write(file, m_writer.set_temperature(temp, wait, first_printing_extruder_id));
+ file.write(m_writer.set_temperature(temp, wait, first_printing_extruder_id));
} else {
// Set temperatures of all the printing extruders.
for (unsigned int tool_id : print.extruders()) {
@@ -1654,7 +1655,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
if (print.config().ooze_prevention.value)
temp += print.config().standby_temperature_delta.value;
if (temp > 0)
- _write(file, m_writer.set_temperature(temp, wait, tool_id));
+ file.write(m_writer.set_temperature(temp, wait, tool_id));
}
}
}
@@ -1891,7 +1892,7 @@ namespace Skirt {
// and performing the extruder specific extrusions together.
void GCode::process_layer(
// Write into the output file.
- FILE *file,
+ GCodeOutputStream &file,
const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z.
const std::vector<LayerToPrint> &layers,
@@ -1977,6 +1978,7 @@ void GCode::process_layer(
}
gcode += this->change_layer(print_z); // this will increase m_layer_index
m_layer = &layer;
+ m_object_layer_over_raft = false;
if (! print.config().layer_gcode.value.empty()) {
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
@@ -2235,8 +2237,13 @@ void GCode::process_layer(
gcode+="; PURGING FINISHED\n";
for (InstanceToPrint &instance_to_print : instances_to_print) {
+ const LayerToPrint &layer_to_print = layers[instance_to_print.layer_id];
+ // To control print speed of the 1st object layer printed over raft interface.
+ bool object_layer_over_raft = layer_to_print.object_layer && layer_to_print.object_layer->id() > 0 &&
+ instance_to_print.print_object.slicing_parameters().raft_layers() == layer_to_print.object_layer->id();
m_config.apply(instance_to_print.print_object.config(), true);
- m_layer = layers[instance_to_print.layer_id].layer();
+ m_layer = layer_to_print.layer();
+ m_object_layer_over_raft = object_layer_over_raft;
if (m_config.avoid_crossing_perimeters)
m_avoid_crossing_perimeters.init_layer(*m_layer);
if (this->config().gcode_label_objects)
@@ -2249,11 +2256,13 @@ void GCode::process_layer(
m_last_obj_copy = this_object_copy;
this->set_origin(unscale(offset));
if (instance_to_print.object_by_extruder.support != nullptr && !print_wipe_extrusions) {
- m_layer = layers[instance_to_print.layer_id].support_layer;
+ m_layer = layer_to_print.support_layer;
+ m_object_layer_over_raft = false;
gcode += this->extrude_support(
// support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, instance_to_print.object_by_extruder.support_extrusion_role));
- m_layer = layers[instance_to_print.layer_id].layer();
+ m_layer = layer_to_print.layer();
+ m_object_layer_over_raft = object_layer_over_raft;
}
//FIXME order islands?
// Sequential tool path ordering of multiple parts within the same object, aka. perimeter tracking (#5511)
@@ -2298,7 +2307,7 @@ void GCode::process_layer(
// printf("G-code after filter:\n%s\n", out.c_str());
#endif /* HAS_PRESSURE_EQUALIZER */
- _write(file, gcode);
+ file.write(gcode);
BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z <<
log_memory_info();
}
@@ -2634,22 +2643,42 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
return gcode;
}
-void GCode::_write(FILE* file, const char *what)
+bool GCode::GCodeOutputStream::is_error() const
+{
+ return ::ferror(this->f);
+}
+
+void GCode::GCodeOutputStream::flush()
+{
+ ::fflush(this->f);
+}
+
+void GCode::GCodeOutputStream::close()
+{
+ if (this->f) {
+ ::fclose(this->f);
+ this->f = nullptr;
+ }
+}
+
+void GCode::GCodeOutputStream::write(const char *what)
{
if (what != nullptr) {
const char* gcode = what;
// writes string to file
- fwrite(gcode, 1, ::strlen(gcode), file);
+ fwrite(gcode, 1, ::strlen(gcode), this->f);
+ //FIXME don't allocate a string, maybe process a batch of lines?
+ m_processor.process_buffer(std::string(gcode));
}
}
-void GCode::_writeln(FILE* file, const std::string &what)
+void GCode::GCodeOutputStream::writeln(const std::string &what)
{
if (! what.empty())
- _write(file, (what.back() == '\n') ? what : (what + '\n'));
+ this->write(what.back() == '\n' ? what : what + '\n');
}
-void GCode::_write_format(FILE* file, const char* format, ...)
+void GCode::GCodeOutputStream::write_format(const char* format, ...)
{
va_list args;
va_start(args, format);
@@ -2673,7 +2702,7 @@ void GCode::_write_format(FILE* file, const char* format, ...)
char *bufptr = buffer_dynamic ? (char*)malloc(buflen) : buffer;
int res = ::vsnprintf(bufptr, buflen, format, args);
if (res > 0)
- _write(file, bufptr);
+ this->write(bufptr);
if (buffer_dynamic)
free(bufptr);
@@ -2705,6 +2734,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
double acceleration;
if (this->on_first_layer() && m_config.first_layer_acceleration.value > 0) {
acceleration = m_config.first_layer_acceleration.value;
+ } else if (this->object_layer_over_raft() && m_config.first_layer_acceleration_over_raft.value > 0) {
+ acceleration = m_config.first_layer_acceleration_over_raft.value;
} else if (m_config.perimeter_acceleration.value > 0 && is_perimeter(path.role())) {
acceleration = m_config.perimeter_acceleration.value;
} else if (m_config.bridge_acceleration.value > 0 && is_bridge(path.role())) {
@@ -2749,6 +2780,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
speed = m_volumetric_speed / path.mm3_per_mm;
if (this->on_first_layer())
speed = m_config.get_abs_value("first_layer_speed", speed);
+ else if (this->object_layer_over_raft())
+ speed = m_config.get_abs_value("first_layer_speed_over_raft", speed);
if (m_config.max_volumetric_speed.value > 0) {
// cap speed with max_volumetric_speed anyway (even if user is not using autospeed)
speed = std::min(
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index 08ab83002..1dbc5b70a 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -125,7 +125,8 @@ public:
m_last_processor_extrusion_role(erNone),
m_layer_count(0),
m_layer_index(-1),
- m_layer(nullptr),
+ m_layer(nullptr),
+ m_object_layer_over_raft(false),
m_volumetric_speed(0),
m_last_pos_defined(false),
m_last_extrusion_role(erNone),
@@ -138,7 +139,7 @@ public:
m_silent_time_estimator_enabled(false),
m_last_obj_copy(nullptr, Point(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max()))
{}
- ~GCode() {}
+ ~GCode() = default;
// throws std::runtime_exception on error,
// throws CanceledException through print->throw_if_canceled().
@@ -183,13 +184,40 @@ public:
};
private:
- void _do_export(Print &print, FILE *file, ThumbnailsGeneratorCallback thumbnail_cb);
+ class GCodeOutputStream {
+ public:
+ GCodeOutputStream(FILE *f, GCodeProcessor &processor) : f(f), m_processor(processor) {}
+ ~GCodeOutputStream() { this->close(); }
+
+ bool is_open() const { return f; }
+ bool is_error() const;
+
+ void flush();
+ void close();
+
+ // Write a string into a file.
+ void write(const std::string& what) { this->write(what.c_str()); }
+ void write(const char* what);
+
+ // Write a string into a file.
+ // Add a newline, if the string does not end with a newline already.
+ // Used to export a custom G-code section processed by the PlaceholderParser.
+ void writeln(const std::string& what);
+
+ // Formats and write into a file the given data.
+ void write_format(const char* format, ...);
+
+ private:
+ FILE *f = nullptr;
+ GCodeProcessor &m_processor;
+ };
+ void _do_export(Print &print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb);
static std::vector<LayerToPrint> collect_layers_to_print(const PrintObject &object);
static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> collect_layers_to_print(const Print &print);
void process_layer(
// Write into the output file.
- FILE *file,
+ GCodeOutputStream &file,
const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z.
const std::vector<LayerToPrint> &layers,
@@ -316,9 +344,11 @@ private:
unsigned int m_layer_count;
// Progress bar indicator. Increments from -1 up to layer_count.
int m_layer_index;
- // Current layer processed. Insequential printing mode, only a single copy will be printed.
+ // Current layer processed. In sequential printing mode, only a single copy will be printed.
// In non-sequential mode, all its copies will be printed.
const Layer* m_layer;
+ // m_layer is an object layer and it is being printed over raft surface.
+ bool m_object_layer_over_raft;
double m_volumetric_speed;
// Support for the extrusion role markers. Which marker is active?
ExtrusionRole m_last_extrusion_role;
@@ -355,24 +385,14 @@ private:
// Processor
GCodeProcessor m_processor;
- // Write a string into a file.
- void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str()); }
- void _write(FILE* file, const char *what);
-
- // Write a string into a file.
- // Add a newline, if the string does not end with a newline already.
- // Used to export a custom G-code section processed by the PlaceholderParser.
- void _writeln(FILE* file, const std::string& what);
-
- // Formats and write into a file the given data.
- void _write_format(FILE* file, const char* format, ...);
-
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
- void print_machine_envelope(FILE *file, Print &print);
- void _print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
- void _print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
+ void print_machine_envelope(GCodeOutputStream &file, Print &print);
+ void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
+ void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
// On the first printing layer. This flag triggers first layer speeds.
bool on_first_layer() const { return m_layer != nullptr && m_layer->id() == 0; }
+ // To control print speed of 1st object layer over raft interface.
+ bool object_layer_over_raft() const { return m_object_layer_over_raft; }
friend ObjectByExtruder& object_by_extruder(
std::map<unsigned int, std::vector<ObjectByExtruder>> &by_extruder,
diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index 0d71a19f5..d16cfb9cf 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -21,6 +21,9 @@
#include <chrono>
+static const float DEFAULT_TOOLPATH_WIDTH = 0.4f;
+static const float DEFAULT_TOOLPATH_HEIGHT = 0.2f;
+
static const float INCHES_TO_MM = 25.4f;
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
@@ -344,20 +347,34 @@ void GCodeProcessor::TimeProcessor::reset()
machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
}
-void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<MoveVertex>& moves)
+struct FilePtr {
+ FilePtr(FILE *f) : f(f) {}
+ ~FilePtr() { this->close(); }
+ void close() {
+ if (this->f) {
+ ::fclose(this->f);
+ this->f = nullptr;
+ }
+ }
+ FILE* f = nullptr;
+};
+
+void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<MoveVertex>& moves, std::vector<size_t>& lines_ends)
{
- boost::nowide::ifstream in(filename);
- if (!in.good())
+ FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
+ if (in.f == nullptr)
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for reading.\n"));
// temporary file to contain modified gcode
std::string out_path = filename + ".postprocess";
- FILE* out = boost::nowide::fopen(out_path.c_str(), "wb");
- if (out == nullptr)
+ FilePtr out{ boost::nowide::fopen(out_path.c_str(), "wb") };
+ if (out.f == nullptr) {
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for writing.\n"));
+ }
auto time_in_minutes = [](float time_in_seconds) {
- return int(::roundf(time_in_seconds / 60.0f));
+ assert(time_in_seconds >= 0.f);
+ return int((time_in_seconds + 0.5f) / 60.0f);
};
auto time_in_last_minute = [](float time_in_seconds) {
@@ -389,7 +406,6 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
return std::string(line_M73);
};
- GCodeReader parser;
std::string gcode_line;
size_t g1_lines_counter = 0;
// keeps track of last exported pair <percent, remaining time>
@@ -408,11 +424,12 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
std::string export_line;
// replace placeholder lines with the proper final value
- auto process_placeholders = [&](const std::string& gcode_line) {
+ // gcode_line is in/out parameter, to reduce expensive memory allocation
+ auto process_placeholders = [&](std::string& gcode_line) {
unsigned int extra_lines_count = 0;
// remove trailing '\n'
- std::string line = gcode_line.substr(0, gcode_line.length() - 1);
+ auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1);
std::string ret;
if (line.length() > 1) {
@@ -453,7 +470,10 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
}
}
- return std::tuple(!ret.empty(), ret.empty() ? gcode_line : ret, (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1);
+ if (! ret.empty())
+ // Not moving the move operator on purpose, so that the gcode_line allocation will grow and it will not be reallocated after handful of lines are processed.
+ gcode_line = ret;
+ return std::tuple(!ret.empty(), (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1);
};
// check for temporary lines
@@ -476,11 +496,19 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
g1_times_cache_it.emplace_back(machine.g1_times_cache.begin());
// add lines M73 to exported gcode
- auto process_line_G1 = [&]() {
+ auto process_line_G1 = [
+ // Lambdas, mostly for string formatting, all with an empty capture block.
+ time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute,
+ &self = std::as_const(*this),
+ // Caches, to be modified
+ &g1_times_cache_it, &last_exported_main, &last_exported_stop,
+ // String output
+ &export_line]
+ (const size_t g1_lines_counter) {
unsigned int exported_lines_count = 0;
- if (export_remaining_time_enabled) {
+ if (self.export_remaining_time_enabled) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
- const TimeMachine& machine = machines[i];
+ const TimeMachine& machine = self.machines[i];
if (machine.enabled) {
// export pair <percent, remaining time>
// Skip all machine.g1_times_cache below g1_lines_counter.
@@ -544,60 +572,81 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
};
// helper function to write to disk
- auto write_string = [&](const std::string& str) {
- fwrite((const void*)export_line.c_str(), 1, export_line.length(), out);
- if (ferror(out)) {
- in.close();
- fclose(out);
+ size_t out_file_pos = 0;
+ lines_ends.clear();
+ auto write_string = [&export_line, &out, &out_path, &out_file_pos, &lines_ends](const std::string& str) {
+ fwrite((const void*)export_line.c_str(), 1, export_line.length(), out.f);
+ if (ferror(out.f)) {
+ out.close();
boost::nowide::remove(out_path.c_str());
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nIs the disk full?\n"));
}
+ for (size_t i = 0; i < export_line.size(); ++ i)
+ if (export_line[i] == '\n')
+ lines_ends.emplace_back(out_file_pos + i + 1);
+ out_file_pos += export_line.size();
export_line.clear();
};
unsigned int line_id = 0;
std::vector<std::pair<unsigned int, unsigned int>> offsets;
- while (std::getline(in, gcode_line)) {
- if (!in.good()) {
- fclose(out);
- throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
- }
-
- ++line_id;
-
- gcode_line += "\n";
- // replace placeholder lines
- auto [processed, result, lines_added_count] = process_placeholders(gcode_line);
- if (processed && lines_added_count > 0)
- offsets.push_back({ line_id, lines_added_count });
- gcode_line = result;
- if (!processed) {
- // remove temporary lines
- if (is_temporary_decoration(gcode_line))
- continue;
-
- // add lines M73 where needed
- parser.parse_line(gcode_line,
- [&](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
- if (line.cmd_is("G1")) {
- unsigned int extra_lines_count = process_line_G1();
- ++g1_lines_counter;
+ {
+ // Read the input stream 64kB at a time, extract lines and process them.
+ std::vector<char> buffer(65536 * 10, 0);
+ // Line buffer.
+ assert(gcode_line.empty());
+ for (;;) {
+ size_t cnt_read = ::fread(buffer.data(), 1, buffer.size(), in.f);
+ if (::ferror(in.f))
+ throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
+ bool eof = cnt_read == 0;
+ auto it = buffer.begin();
+ auto it_bufend = buffer.begin() + cnt_read;
+ while (it != it_bufend || (eof && ! gcode_line.empty())) {
+ // Find end of line.
+ bool eol = false;
+ auto it_end = it;
+ for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ;
+ // End of line is indicated also if end of file was reached.
+ eol |= eof && it_end == it_bufend;
+ gcode_line.insert(gcode_line.end(), it, it_end);
+ if (eol) {
+ ++line_id;
+
+ gcode_line += "\n";
+ // replace placeholder lines
+ auto [processed, lines_added_count] = process_placeholders(gcode_line);
+ if (processed && lines_added_count > 0)
+ offsets.push_back({ line_id, lines_added_count });
+ if (! processed && ! is_temporary_decoration(gcode_line) && GCodeReader::GCodeLine::cmd_is(gcode_line, "G1")) {
+ // remove temporary lines, add lines M73 where needed
+ unsigned int extra_lines_count = process_line_G1(g1_lines_counter ++);
if (extra_lines_count > 0)
offsets.push_back({ line_id, extra_lines_count });
}
- });
- }
- export_line += gcode_line;
- if (export_line.length() > 65535)
- write_string(export_line);
+ export_line += gcode_line;
+ if (export_line.length() > 65535)
+ write_string(export_line);
+ gcode_line.clear();
+ }
+ // Skip EOL.
+ it = it_end;
+ if (it != it_bufend && *it == '\r')
+ ++ it;
+ if (it != it_bufend && *it == '\n')
+ ++ it;
+ }
+ if (eof)
+ break;
+ }
}
if (!export_line.empty())
write_string(export_line);
- fclose(out);
+ out.close();
in.close();
// updates moves' gcode ids which have been modified by the insertion of the M73 lines
@@ -698,7 +747,9 @@ void GCodeProcessor::Result::reset() {
}
#else
void GCodeProcessor::Result::reset() {
- moves = std::vector<GCodeProcessor::MoveVertex>();
+
+ moves.clear();
+ lines_ends.clear();
bed_shape = Pointfs();
settings_ids.reset();
extruders_count = 0;
@@ -777,6 +828,9 @@ bool GCodeProcessor::contains_reserved_tags(const std::string& gcode, unsigned i
}
GCodeProcessor::GCodeProcessor()
+#if ENABLE_FIX_PREVIEW_OPTIONS_Z
+: m_options_z_corrector(m_result)
+#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
{
reset();
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_main_mask = "M73 P%s R%s\n";
@@ -951,10 +1005,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
}
// replace missing values with default
- std::string default_color = "#FF8000";
for (size_t i = 0; i < m_result.extruder_colors.size(); ++i) {
if (m_result.extruder_colors[i].empty())
- m_result.extruder_colors[i] = default_color;
+ m_result.extruder_colors[i] = "#FF8000";
}
m_extruder_colors.resize(m_result.extruder_colors.size());
@@ -1139,7 +1192,6 @@ void GCodeProcessor::reset()
m_cp_color.reset();
m_producer = EProducer::Unknown;
- m_producers_enabled = false;
m_time_processor.reset();
m_used_filaments.reset();
@@ -1152,6 +1204,10 @@ void GCodeProcessor::reset()
m_last_default_color_id = 0;
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
+#if ENABLE_FIX_PREVIEW_OPTIONS_Z
+ m_options_z_corrector.reset();
+#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
+
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
m_mm3_per_mm_compare.reset();
m_height_compare.reset();
@@ -1159,27 +1215,26 @@ void GCodeProcessor::reset()
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
}
-void GCodeProcessor::process_file(const std::string& filename, bool apply_postprocess, std::function<void()> cancel_callback)
+void GCodeProcessor::process_file(const std::string& filename, std::function<void()> cancel_callback)
{
- auto last_cancel_callback_time = std::chrono::high_resolution_clock::now();
-
CNumericLocalesSetter locales_setter;
#if ENABLE_GCODE_VIEWER_STATISTICS
- auto start_time = std::chrono::high_resolution_clock::now();
+ m_start_time = std::chrono::high_resolution_clock::now();
#endif // ENABLE_GCODE_VIEWER_STATISTICS
// pre-processing
// parse the gcode file to detect its producer
- if (m_producers_enabled) {
+ {
m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
const std::string_view cmd = line.cmd();
- if (cmd.length() == 0) {
+ if (cmd.empty()) {
const std::string_view comment = line.comment();
if (comment.length() > 1 && detect_producer(comment))
m_parser.quit_parsing();
}
});
+ m_parser.reset();
// if the gcode was produced by PrusaSlicer,
// extract the config from it
@@ -1201,18 +1256,45 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
m_result.id = ++s_result_id;
// 1st move must be a dummy move
m_result.moves.emplace_back(MoveVertex());
- m_parser.parse_file(filename, [this, cancel_callback, &last_cancel_callback_time](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
- if (cancel_callback != nullptr) {
- // call the cancel callback every 100 ms
- auto curr_time = std::chrono::high_resolution_clock::now();
- if (std::chrono::duration_cast<std::chrono::milliseconds>(curr_time - last_cancel_callback_time).count() > 100) {
+ size_t parse_line_callback_cntr = 10000;
+ m_parser.parse_file(filename, [this, cancel_callback, &parse_line_callback_cntr](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
+ if (-- parse_line_callback_cntr == 0) {
+ // Don't call the cancel_callback() too often, do it every at every 10000'th line.
+ parse_line_callback_cntr = 10000;
+ if (cancel_callback)
cancel_callback();
- last_cancel_callback_time = curr_time;
- }
}
- process_gcode_line(line);
- });
+ this->process_gcode_line(line, true);
+ });
+
+ this->finalize();
+}
+
+void GCodeProcessor::initialize(const std::string& filename)
+{
+ assert(is_decimal_separator_point());
+
+#if ENABLE_GCODE_VIEWER_STATISTICS
+ m_start_time = std::chrono::high_resolution_clock::now();
+#endif // ENABLE_GCODE_VIEWER_STATISTICS
+
+ // process gcode
+ m_result.filename = filename;
+ m_result.id = ++s_result_id;
+ // 1st move must be a dummy move
+ m_result.moves.emplace_back(MoveVertex());
+}
+void GCodeProcessor::process_buffer(const std::string &buffer)
+{
+ //FIXME maybe cache GCodeLine gline to be over multiple parse_buffer() invocations.
+ m_parser.parse_buffer(buffer, [this](GCodeReader&, const GCodeReader::GCodeLine& line) {
+ this->process_gcode_line(line, false);
+ });
+}
+
+void GCodeProcessor::finalize()
+{
// update width/height of wipe moves
for (MoveVertex& move : m_result.moves) {
if (move.type == EMoveType::Wipe) {
@@ -1234,10 +1316,6 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
update_estimated_times_stats();
- // post-process to add M73 lines into the gcode
- if (apply_postprocess)
- m_time_processor.post_process(filename, m_result.moves);
-
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
std::cout << "\n";
m_mm3_per_mm_compare.output();
@@ -1245,8 +1323,9 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
m_width_compare.output();
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
+ m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends);
#if ENABLE_GCODE_VIEWER_STATISTICS
- m_result.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start_time).count();
+ m_result.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_start_time).count();
#endif // ENABLE_GCODE_VIEWER_STATISTICS
}
@@ -1340,7 +1419,7 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
if (pos != cmt.npos) {
std::string data_str = cmt.substr(pos + 1);
std::vector<std::string> values_str;
- boost::split(values_str, data_str, boost::is_any_of("|"), boost::token_compress_on);
+ boost::split(values_str, data_str, boost::is_any_of("|,"), boost::token_compress_on);
for (const std::string& s : values_str) {
out.emplace_back(static_cast<float>(string_to_double_decimal_point(s)));
}
@@ -1364,10 +1443,16 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
m_result.filament_densities.clear();
extract_floats(comment, "filamentDensities", m_result.filament_densities);
}
+ else if (comment.find("extruderDiameter") != comment.npos) {
+ std::vector<float> extruder_diameters;
+ extract_floats(comment, "extruderDiameter", extruder_diameters);
+ m_result.extruders_count = extruder_diameters.size();
+ }
}
});
- m_result.extruders_count = std::max<size_t>(1, std::min(m_result.filament_diameters.size(), m_result.filament_densities.size()));
+ if (m_result.extruders_count == 0)
+ m_result.extruders_count = std::max<size_t>(1, std::min(m_result.filament_diameters.size(), m_result.filament_densities.size()));
if (bed_size.is_defined()) {
m_result.bed_shape = {
@@ -1379,7 +1464,7 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
}
}
-void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line)
+void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled)
{
/* std::cout << line.raw() << std::endl; */
@@ -1391,61 +1476,170 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line)
const std::string_view cmd = line.cmd();
if (cmd.length() > 1) {
// process command lines
- switch (::toupper(cmd[0]))
+ switch (cmd[0])
{
+ case 'g':
case 'G':
- {
- switch (::atoi(&cmd[1]))
- {
- case 0: { process_G0(line); break; } // Move
- case 1: { process_G1(line); break; } // Move
- case 10: { process_G10(line); break; } // Retract
- case 11: { process_G11(line); break; } // Unretract
- case 20: { process_G20(line); break; } // Set Units to Inches
- case 21: { process_G21(line); break; } // Set Units to Millimeters
- case 22: { process_G22(line); break; } // Firmware controlled retract
- case 23: { process_G23(line); break; } // Firmware controlled unretract
- case 28: { process_G28(line); break; } // Move to origin
- case 90: { process_G90(line); break; } // Set to Absolute Positioning
- case 91: { process_G91(line); break; } // Set to Relative Positioning
- case 92: { process_G92(line); break; } // Set Position
- default: { break; }
+ switch (cmd.size()) {
+ case 2:
+ switch (cmd[1]) {
+ case '0': { process_G0(line); break; } // Move
+ case '1': { process_G1(line); break; } // Move
+ default: break;
+ }
+ break;
+ case 3:
+ switch (cmd[1]) {
+ case '1':
+ switch (cmd[2]) {
+ case '0': { process_G10(line); break; } // Retract
+ case '1': { process_G11(line); break; } // Unretract
+ default: break;
+ }
+ break;
+ case '2':
+ switch (cmd[2]) {
+ case '0': { process_G20(line); break; } // Set Units to Inches
+ case '1': { process_G21(line); break; } // Set Units to Millimeters
+ case '2': { process_G22(line); break; } // Firmware controlled retract
+ case '3': { process_G23(line); break; } // Firmware controlled unretract
+ case '8': { process_G28(line); break; } // Move to origin
+ default: break;
+ }
+ break;
+ case '9':
+ switch (cmd[2]) {
+ case '0': { process_G90(line); break; } // Set to Absolute Positioning
+ case '1': { process_G91(line); break; } // Set to Relative Positioning
+ case '2': { process_G92(line); break; } // Set Position
+ default: break;
+ }
+ break;
}
break;
+ default:
+ break;
}
+ break;
+ case 'm':
case 'M':
- {
- switch (::atoi(&cmd[1]))
- {
- case 1: { process_M1(line); break; } // Sleep or Conditional stop
- case 82: { process_M82(line); break; } // Set extruder to absolute mode
- case 83: { process_M83(line); break; } // Set extruder to relative mode
- case 104: { process_M104(line); break; } // Set extruder temperature
- case 106: { process_M106(line); break; } // Set fan speed
- case 107: { process_M107(line); break; } // Disable fan
- case 108: { process_M108(line); break; } // Set tool (Sailfish)
- case 109: { process_M109(line); break; } // Set extruder temperature and wait
- case 132: { process_M132(line); break; } // Recall stored home offsets
- case 135: { process_M135(line); break; } // Set tool (MakerWare)
- case 201: { process_M201(line); break; } // Set max printing acceleration
- case 203: { process_M203(line); break; } // Set maximum feedrate
- case 204: { process_M204(line); break; } // Set default acceleration
- case 205: { process_M205(line); break; } // Advanced settings
- case 221: { process_M221(line); break; } // Set extrude factor override percentage
- case 401: { process_M401(line); break; } // Repetier: Store x, y and z position
- case 402: { process_M402(line); break; } // Repetier: Go to stored position
- case 566: { process_M566(line); break; } // Set allowable instantaneous speed change
- case 702: { process_M702(line); break; } // Unload the current filament into the MK3 MMU2 unit at the end of print.
- default: { break; }
+ switch (cmd.size()) {
+ case 2:
+ switch (cmd[1]) {
+ case '1': { process_M1(line); break; } // Sleep or Conditional stop
+ default: break;
}
break;
- }
- case 'T':
- {
- process_T(line); // Select Tool
+ case 3:
+ switch (cmd[1]) {
+ case '8':
+ switch (cmd[2]) {
+ case '2': { process_M82(line); break; } // Set extruder to absolute mode
+ case '3': { process_M83(line); break; } // Set extruder to relative mode
+ default: break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case 4:
+ switch (cmd[1]) {
+ case '1':
+ switch (cmd[2]) {
+ case '0':
+ switch (cmd[3]) {
+ case '4': { process_M104(line); break; } // Set extruder temperature
+ case '6': { process_M106(line); break; } // Set fan speed
+ case '7': { process_M107(line); break; } // Disable fan
+ case '8': { process_M108(line); break; } // Set tool (Sailfish)
+ case '9': { process_M109(line); break; } // Set extruder temperature and wait
+ default: break;
+ }
+ break;
+ case '3':
+ switch (cmd[3]) {
+ case '2': { process_M132(line); break; } // Recall stored home offsets
+ case '5': { process_M135(line); break; } // Set tool (MakerWare)
+ default: break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case '2':
+ switch (cmd[2]) {
+ case '0':
+ switch (cmd[3]) {
+ case '1': { process_M201(line); break; } // Set max printing acceleration
+ case '3': { process_M203(line); break; } // Set maximum feedrate
+ case '4': { process_M204(line); break; } // Set default acceleration
+ case '5': { process_M205(line); break; } // Advanced settings
+ default: break;
+ }
+ break;
+ case '2':
+ switch (cmd[3]) {
+ case '1': { process_M221(line); break; } // Set extrude factor override percentage
+ default: break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case '4':
+ switch (cmd[2]) {
+ case '0':
+ switch (cmd[3]) {
+ case '1': { process_M401(line); break; } // Repetier: Store x, y and z position
+ case '2': { process_M402(line); break; } // Repetier: Go to stored position
+ default: break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case '5':
+ switch (cmd[2]) {
+ case '6':
+ switch (cmd[3]) {
+ case '6': { process_M566(line); break; } // Set allowable instantaneous speed change
+ default: break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case '7':
+ switch (cmd[2]) {
+ case '0':
+ switch (cmd[3]) {
+ case '2': { process_M702(line); break; } // Unload the current filament into the MK3 MMU2 unit at the end of print.
+ default: break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
break;
}
- default: { break; }
+ break;
+ case 't':
+ case 'T':
+ process_T(line); // Select Tool
+ break;
+ default:
+ break;
}
}
else {
@@ -1453,7 +1647,7 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line)
if (comment.length() > 2 && comment.front() == ';')
// Process tags embedded into comments. Tag comments always start at the start of a line
// with a comment and continue with a tag without any whitespace separator.
- process_tags(comment.substr(1));
+ process_tags(comment.substr(1), producers_enabled);
}
}
@@ -1502,10 +1696,10 @@ template<typename T>
}
}
-void GCodeProcessor::process_tags(const std::string_view comment)
+void GCodeProcessor::process_tags(const std::string_view comment, bool producers_enabled)
{
// producers tags
- if (m_producers_enabled && process_producers_tags(comment))
+ if (producers_enabled && process_producers_tags(comment))
return;
// extrusion role tag
@@ -1529,7 +1723,7 @@ void GCodeProcessor::process_tags(const std::string_view comment)
return;
}
- if (!m_producers_enabled || m_producer == EProducer::PrusaSlicer) {
+ if (!producers_enabled || m_producer == EProducer::PrusaSlicer) {
// height tag
if (boost::starts_with(comment, reserved_tag(ETags::Height))) {
if (!parse_number(comment.substr(reserved_tag(ETags::Height).size()), m_forced_height))
@@ -1619,6 +1813,9 @@ void GCodeProcessor::process_tags(const std::string_view comment)
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::ColorChange, extruder_id + 1, color, "" };
m_result.custom_gcode_per_print_z.emplace_back(item);
+#if ENABLE_FIX_PREVIEW_OPTIONS_Z
+ m_options_z_corrector.set();
+#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
process_custom_gcode_time(CustomGCode::ColorChange);
process_filaments(CustomGCode::ColorChange);
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
@@ -1638,6 +1835,9 @@ void GCodeProcessor::process_tags(const std::string_view comment)
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::PausePrint, m_extruder_id + 1, "", "" };
m_result.custom_gcode_per_print_z.emplace_back(item);
+#if ENABLE_FIX_PREVIEW_OPTIONS_Z
+ m_options_z_corrector.set();
+#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
process_custom_gcode_time(CustomGCode::PausePrint);
return;
@@ -1649,6 +1849,9 @@ void GCodeProcessor::process_tags(const std::string_view comment)
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::Custom, m_extruder_id + 1, "", "" };
m_result.custom_gcode_per_print_z.emplace_back(item);
+#if ENABLE_FIX_PREVIEW_OPTIONS_Z
+ m_options_z_corrector.set();
+#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
return;
}
@@ -2210,9 +2413,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
return;
EMoveType type = move_type(delta_pos);
- if (type == EMoveType::Extrude && m_end_position[Z] == 0.0f)
- type = EMoveType::Travel;
-
if (type == EMoveType::Extrude) {
float delta_xyz = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]));
float volume_extruded_filament = area_filament_cross_section * delta_pos[E];
@@ -2232,10 +2432,23 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
else {
if (m_end_position[Z] > m_extruded_last_z + EPSILON) {
m_height = m_end_position[Z] - m_extruded_last_z;
+#if !ENABLE_FIX_PREVIEW_OPTIONS_Z
m_extruded_last_z = m_end_position[Z];
+#endif // !ENABLE_FIX_PREVIEW_OPTIONS_Z
}
}
+ if (m_height == 0.0f)
+ m_height = DEFAULT_TOOLPATH_HEIGHT;
+
+ if (m_end_position[Z] == 0.0f)
+ m_end_position[Z] = m_height;
+
+#if ENABLE_FIX_PREVIEW_OPTIONS_Z
+ m_extruded_last_z = m_end_position[Z];
+ m_options_z_corrector.update(m_height);
+#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
+
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
m_height_compare.update(m_height, m_extrusion_role);
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
@@ -2252,17 +2465,17 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
// cross section: rectangle + 2 semicircles
m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast<float>(1.0 - 0.25 * M_PI) * m_height;
+ if (m_width == 0.0f)
+ m_width = DEFAULT_TOOLPATH_WIDTH;
+
// clamp width to avoid artifacts which may arise from wrong values of m_height
- m_width = std::min(m_width, std::max(1.0f, 4.0f * m_height));
+ m_width = std::min(m_width, std::max(2.0f, 4.0f * m_height));
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
m_width_compare.update(m_width, m_extrusion_role);
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
}
- if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f))
- type = EMoveType::Travel;
-
// time estimate section
auto move_length = [](const AxisCoords& delta_pos) {
float sq_xyz_length = sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]);
@@ -2916,7 +3129,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
m_line_id + 1 :
((type == EMoveType::Seam) ? m_last_line_id : m_line_id);
- MoveVertex vertex = {
+ m_result.moves.push_back({
m_last_line_id,
type,
m_extrusion_role,
@@ -2931,8 +3144,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
m_fan_speed,
m_extruder_temps[m_extruder_id],
static_cast<float>(m_result.moves.size())
- };
- m_result.moves.emplace_back(vertex);
+ });
// stores stop time placeholders for later use
if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) {
diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp
index 4fcdd8df3..040f7432a 100644
--- a/src/libslic3r/GCode/GCodeProcessor.hpp
+++ b/src/libslic3r/GCode/GCodeProcessor.hpp
@@ -306,7 +306,7 @@ namespace Slic3r {
// post process the file with the given filename to add remaining time lines M73
// and updates moves' gcode ids accordingly
- void post_process(const std::string& filename, std::vector<MoveVertex>& moves);
+ void post_process(const std::string& filename, std::vector<MoveVertex>& moves, std::vector<size_t>& lines_ends);
};
struct UsedFilaments // filaments per ColorChange
@@ -350,6 +350,8 @@ namespace Slic3r {
std::string filename;
unsigned int id;
std::vector<MoveVertex> moves;
+ // Positions of ends of lines of the final G-code this->filename after TimeProcessor::post_process() finalizes the G-code.
+ std::vector<size_t> lines_ends;
Pointfs bed_shape;
SettingsIds settings_ids;
size_t extruders_count;
@@ -388,6 +390,45 @@ namespace Slic3r {
bool has_first_vertex() const { return m_first_vertex.has_value(); }
};
+#if ENABLE_FIX_PREVIEW_OPTIONS_Z
+ // Helper class used to fix the z for color change, pause print and
+ // custom gcode markes
+ class OptionsZCorrector
+ {
+ Result& m_result;
+ std::optional<size_t> m_move_id;
+ std::optional<size_t> m_custom_gcode_per_print_z_id;
+
+ public:
+ explicit OptionsZCorrector(Result& result) : m_result(result) {
+ }
+
+ void set() {
+ m_move_id = m_result.moves.size() - 1;
+ m_custom_gcode_per_print_z_id = m_result.custom_gcode_per_print_z.size() - 1;
+ }
+
+ void update(float height) {
+ if (!m_move_id.has_value() || !m_custom_gcode_per_print_z_id.has_value())
+ return;
+
+ const Vec3f position = m_result.moves.back().position;
+
+ MoveVertex& move = m_result.moves.emplace_back(m_result.moves[*m_move_id]);
+ move.position = position;
+ move.height = height;
+ m_result.moves.erase(m_result.moves.begin() + *m_move_id);
+ m_result.custom_gcode_per_print_z[*m_custom_gcode_per_print_z_id].print_z = position.z();
+ reset();
+ }
+
+ void reset() {
+ m_move_id.reset();
+ m_custom_gcode_per_print_z_id.reset();
+ }
+ };
+#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
+
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
struct DataChecker
{
@@ -492,9 +533,15 @@ namespace Slic3r {
CpColor m_cp_color;
bool m_use_volumetric_e;
SeamsDetector m_seams_detector;
+#if ENABLE_FIX_PREVIEW_OPTIONS_Z
+ OptionsZCorrector m_options_z_corrector;
+#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
size_t m_last_default_color_id;
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
+#if ENABLE_GCODE_VIEWER_STATISTICS
+ std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
+#endif // ENABLE_GCODE_VIEWER_STATISTICS
enum class EProducer
{
@@ -511,7 +558,6 @@ namespace Slic3r {
static const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> Producers;
EProducer m_producer;
- bool m_producers_enabled;
TimeProcessor m_time_processor;
UsedFilaments m_used_filaments;
@@ -534,7 +580,6 @@ namespace Slic3r {
return m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled;
}
void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; }
- void enable_producers(bool enabled) { m_producers_enabled = enabled; }
void reset();
const Result& get_result() const { return m_result; }
@@ -542,7 +587,12 @@ namespace Slic3r {
// Process the gcode contained in the file with the given filename
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
- void process_file(const std::string& filename, bool apply_postprocess, std::function<void()> cancel_callback = nullptr);
+ void process_file(const std::string& filename, std::function<void()> cancel_callback = nullptr);
+
+ // Streaming interface, for processing G-codes just generated by PrusaSlicer in a pipelined fashion.
+ void initialize(const std::string& filename);
+ void process_buffer(const std::string& buffer);
+ void finalize();
float get_time(PrintEstimatedStatistics::ETimeMode mode) const;
std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const;
@@ -555,10 +605,10 @@ namespace Slic3r {
private:
void apply_config(const DynamicPrintConfig& config);
void apply_config_simplify3d(const std::string& filename);
- void process_gcode_line(const GCodeReader::GCodeLine& line);
+ void process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled);
// Process tags embedded into comments
- void process_tags(const std::string_view comment);
+ void process_tags(const std::string_view comment, bool producers_enabled);
bool process_producers_tags(const std::string_view comment);
bool process_prusaslicer_tags(const std::string_view comment);
bool process_cura_tags(const std::string_view comment);
diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp
index 7dc3c9a8a..4c4bebee4 100644
--- a/src/libslic3r/GCodeReader.cpp
+++ b/src/libslic3r/GCodeReader.cpp
@@ -2,6 +2,7 @@
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/nowide/fstream.hpp>
+#include <charconv>
#include <fstream>
#include <iostream>
#include <iomanip>
@@ -32,7 +33,7 @@ void GCodeReader::apply_config(const DynamicPrintConfig &config)
m_extrusion_axis = get_extrusion_axis_char(m_config);
}
-const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command)
+const char* GCodeReader::parse_line_internal(const char *ptr, const char *end, GCodeLine &gline, std::pair<const char*, const char*> &command)
{
PROFILE_FUNC();
@@ -70,9 +71,16 @@ const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline,
}
if (axis != NUM_AXES_WITH_UNKNOWN) {
// Try to parse the numeric value.
+#ifdef WIN32
+ double v;
+ auto [pend, ec] = std::from_chars(++ c, end, v);
+ if (pend != c && is_end_of_word(*pend)) {
+#else
+ // The older version of GCC and Clang support std::from_chars just for integers, so strtod we used it instead.
char *pend = nullptr;
double v = strtod(++ c, &pend);
if (pend != nullptr && is_end_of_word(*pend)) {
+#endif
// The axis value has been parsed correctly.
if (axis != UNKNOWN_AXIS)
gline.m_axis[int(axis)] = float(v);
@@ -125,13 +133,42 @@ void GCodeReader::update_coordinates(GCodeLine &gline, std::pair<const char*, co
}
}
-void GCodeReader::parse_file(const std::string &file, callback_t callback)
+bool GCodeReader::parse_file(const std::string &file, callback_t callback)
{
boost::nowide::ifstream f(file);
+ f.sync_with_stdio(false);
+ std::vector<char> buffer(65536 * 10, 0);
std::string line;
m_parsing = true;
- while (m_parsing && std::getline(f, line))
- this->parse_line(line, callback);
+ GCodeLine gline;
+ while (m_parsing && ! f.eof()) {
+ f.read(buffer.data(), buffer.size());
+ if (! f.eof() && ! f.good())
+ // Reading the input file failed.
+ return false;
+ auto it = buffer.begin();
+ auto it_bufend = buffer.begin() + f.gcount();
+ while (it != it_bufend) {
+ bool eol = false;
+ auto it_end = it;
+ for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ;
+ eol |= f.eof() && it_end == it_bufend;
+ if (eol) {
+ gline.reset();
+ if (line.empty())
+ this->parse_line(&(*it), &(*it_end), gline, callback);
+ else {
+ line.insert(line.end(), it, it_end);
+ this->parse_line(line.c_str(), line.c_str() + line.size(), gline, callback);
+ line.clear();
+ }
+ } else
+ line.insert(line.end(), it, it_end);
+ // Skip all the empty lines.
+ for (it = it_end; it != it_bufend && (*it == '\r' || *it == '\n'); ++ it) ;
+ }
+ }
+ return true;
}
bool GCodeReader::GCodeLine::has(char axis) const
diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp
index 6b58608e6..15376c0fc 100644
--- a/src/libslic3r/GCodeReader.hpp
+++ b/src/libslic3r/GCodeReader.hpp
@@ -44,11 +44,7 @@ public:
float y = this->has(Y) ? (this->y() - reader.y()) : 0;
return sqrt(x*x + y*y);
}
- bool cmd_is(const char *cmd_test) const {
- const char *cmd = GCodeReader::skip_whitespaces(m_raw.c_str());
- size_t len = strlen(cmd_test);
- return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]);
- }
+ bool cmd_is(const char *cmd_test) const { return cmd_is(m_raw, cmd_test); }
bool extruding(const GCodeReader &reader) const { return this->cmd_is("G1") && this->dist_E(reader) > 0; }
bool retracting(const GCodeReader &reader) const { return this->cmd_is("G1") && this->dist_E(reader) < 0; }
bool travel() const { return this->cmd_is("G1") && ! this->has(E); }
@@ -66,6 +62,12 @@ public:
float e() const { return m_axis[E]; }
float f() const { return m_axis[F]; }
+ static bool cmd_is(const std::string &gcode_line, const char *cmd_test) {
+ const char *cmd = GCodeReader::skip_whitespaces(gcode_line.c_str());
+ size_t len = strlen(cmd_test);
+ return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]);
+ }
+
private:
std::string m_raw;
float m_axis[NUM_AXES];
@@ -75,7 +77,8 @@ public:
typedef std::function<void(GCodeReader&, const GCodeLine&)> callback_t;
- GCodeReader() : m_verbose(false), m_extrusion_axis('E') { memset(m_position, 0, sizeof(m_position)); }
+ GCodeReader() : m_verbose(false), m_extrusion_axis('E') { this->reset(); }
+ void reset() { memset(m_position, 0, sizeof(m_position)); }
void apply_config(const GCodeConfig &config);
void apply_config(const DynamicPrintConfig &config);
@@ -83,11 +86,12 @@ public:
void parse_buffer(const std::string &buffer, Callback callback)
{
const char *ptr = buffer.c_str();
+ const char *end = ptr + buffer.size();
GCodeLine gline;
m_parsing = true;
while (m_parsing && *ptr != 0) {
gline.reset();
- ptr = this->parse_line(ptr, gline, callback);
+ ptr = this->parse_line(ptr, end, gline, callback);
}
}
@@ -95,20 +99,21 @@ public:
{ this->parse_buffer(buffer, [](GCodeReader&, const GCodeReader::GCodeLine&){}); }
template<typename Callback>
- const char* parse_line(const char *ptr, GCodeLine &gline, Callback &callback)
+ const char* parse_line(const char *ptr, const char *end, GCodeLine &gline, Callback &callback)
{
std::pair<const char*, const char*> cmd;
- const char *end = parse_line_internal(ptr, gline, cmd);
+ const char *line_end = parse_line_internal(ptr, end, gline, cmd);
callback(*this, gline);
update_coordinates(gline, cmd);
- return end;
+ return line_end;
}
template<typename Callback>
void parse_line(const std::string &line, Callback callback)
- { GCodeLine gline; this->parse_line(line.c_str(), gline, callback); }
+ { GCodeLine gline; this->parse_line(line.c_str(), line.c_str() + line.size(), gline, callback); }
- void parse_file(const std::string &file, callback_t callback);
+ // Returns false if reading the file failed.
+ bool parse_file(const std::string &file, callback_t callback);
void quit_parsing() { m_parsing = false; }
float& x() { return m_position[X]; }
@@ -127,7 +132,7 @@ public:
// void set_extrusion_axis(char axis) { m_extrusion_axis = axis; }
private:
- const char* parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command);
+ const char* parse_line_internal(const char *ptr, const char *end, GCodeLine &gline, std::pair<const char*, const char*> &command);
void update_coordinates(GCodeLine &gline, std::pair<const char*, const char*> &command);
static bool is_whitespace(char c) { return c == ' ' || c == '\t'; }
diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp
index 24549fd89..c97180982 100644
--- a/src/libslic3r/GCodeWriter.cpp
+++ b/src/libslic3r/GCodeWriter.cpp
@@ -1,17 +1,21 @@
#include "GCodeWriter.hpp"
#include "CustomGCode.hpp"
#include <algorithm>
+#include <charconv>
#include <iomanip>
#include <iostream>
#include <map>
#include <assert.h>
+#define XYZF_EXPORT_DIGITS 3
+#define E_EXPORT_DIGITS 5
+
#define FLAVOR_IS(val) this->config.gcode_flavor == val
#define FLAVOR_IS_NOT(val) this->config.gcode_flavor != val
#define COMMENT(comment) if (this->config.gcode_comments && !comment.empty()) gcode << " ; " << comment;
#define PRECISION(val, precision) std::fixed << std::setprecision(precision) << (val)
-#define XYZF_NUM(val) PRECISION(val, 3)
-#define E_NUM(val) PRECISION(val, 5)
+#define XYZF_NUM(val) PRECISION(val, XYZF_EXPORT_DIGITS)
+#define E_NUM(val) PRECISION(val, E_EXPORT_DIGITS)
namespace Slic3r {
@@ -288,16 +292,89 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id)
return gcode.str();
}
+class G1Writer {
+private:
+ static constexpr const size_t buflen = 256;
+ char buf[buflen];
+ char *buf_end;
+ std::to_chars_result ptr_err;
+
+public:
+ G1Writer() {
+ this->buf[0] = 'G';
+ this->buf[1] = '1';
+ this->buf_end = this->buf + buflen;
+ this->ptr_err.ptr = this->buf + 2;
+ }
+
+ void emit_axis(const char axis, const double v, size_t digits) {
+ *ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = axis;
+#ifdef WIN32
+ this->ptr_err = std::to_chars(this->ptr_err.ptr, this->buf_end, v, std::chars_format::fixed, digits);
+#else
+ int buf_capacity = int(this->buf_end - this->ptr_err.ptr);
+ int ret = snprintf(this->ptr_err.ptr, buf_capacity, "%.*lf", int(digits), v);
+ if (ret <= 0 || ret > buf_capacity)
+ ptr_err.ec = std::errc::value_too_large;
+ else
+ this->ptr_err.ptr = this->ptr_err.ptr + ret;
+#endif
+ }
+
+ void emit_xy(const Vec2d &point) {
+ this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
+ this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
+ }
+
+ void emit_xyz(const Vec3d &point) {
+ this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
+ this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
+ this->emit_z(point.z());
+ }
+
+ void emit_z(const double z) {
+ this->emit_axis('Z', z, XYZF_EXPORT_DIGITS);
+ }
+
+ void emit_e(const std::string &axis, double v) {
+ if (! axis.empty()) {
+ // not gcfNoExtrusion
+ this->emit_axis(axis[0], v, E_EXPORT_DIGITS);
+ }
+ }
+
+ void emit_f(double speed) {
+ this->emit_axis('F', speed, XYZF_EXPORT_DIGITS);
+ }
+
+ void emit_string(const std::string &s) {
+ strncpy(ptr_err.ptr, s.c_str(), s.size());
+ ptr_err.ptr += s.size();
+ }
+
+ void emit_comment(bool allow_comments, const std::string &comment) {
+ if (allow_comments && ! comment.empty()) {
+ *ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = ';'; *ptr_err.ptr ++ = ' ';
+ this->emit_string(comment);
+ }
+ }
+
+ std::string string() {
+ *ptr_err.ptr ++ = '\n';
+ return std::string(this->buf, ptr_err.ptr - buf);
+ }
+};
+
std::string GCodeWriter::set_speed(double F, const std::string &comment, const std::string &cooling_marker) const
{
assert(F > 0.);
assert(F < 100000.);
- std::ostringstream gcode;
- gcode << "G1 F" << XYZF_NUM(F);
- COMMENT(comment);
- gcode << cooling_marker;
- gcode << "\n";
- return gcode.str();
+
+ G1Writer w;
+ w.emit_f(F);
+ w.emit_comment(this->config.gcode_comments, comment);
+ w.emit_string(cooling_marker);
+ return w.string();
}
std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment)
@@ -305,13 +382,11 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com
m_pos(0) = point(0);
m_pos(1) = point(1);
- std::ostringstream gcode;
- gcode << "G1 X" << XYZF_NUM(point(0))
- << " Y" << XYZF_NUM(point(1))
- << " F" << XYZF_NUM(this->config.travel_speed.value * 60.0);
- COMMENT(comment);
- gcode << "\n";
- return gcode.str();
+ G1Writer w;
+ w.emit_xy(point);
+ w.emit_f(this->config.travel_speed.value * 60.0);
+ w.emit_comment(this->config.gcode_comments, comment);
+ return w.string();
}
std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &comment)
@@ -340,14 +415,11 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
m_lifted = 0;
m_pos = point;
- std::ostringstream gcode;
- gcode << "G1 X" << XYZF_NUM(point(0))
- << " Y" << XYZF_NUM(point(1))
- << " Z" << XYZF_NUM(point(2))
- << " F" << XYZF_NUM(this->config.travel_speed.value * 60.0);
- COMMENT(comment);
- gcode << "\n";
- return gcode.str();
+ G1Writer w;
+ w.emit_xyz(point);
+ w.emit_f(this->config.travel_speed.value * 60.0);
+ w.emit_comment(this->config.gcode_comments, comment);
+ return w.string();
}
std::string GCodeWriter::travel_to_z(double z, const std::string &comment)
@@ -377,12 +449,11 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment)
if (speed == 0.)
speed = this->config.travel_speed.value;
- std::ostringstream gcode;
- gcode << "G1 Z" << XYZF_NUM(z)
- << " F" << XYZF_NUM(speed * 60.0);
- COMMENT(comment);
- gcode << "\n";
- return gcode.str();
+ G1Writer w;
+ w.emit_z(z);
+ w.emit_f(speed * 60.0);
+ w.emit_comment(this->config.gcode_comments, comment);
+ return w.string();
}
bool GCodeWriter::will_move_z(double z) const
@@ -402,16 +473,12 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std:
m_pos(0) = point(0);
m_pos(1) = point(1);
m_extruder->extrude(dE);
-
- std::ostringstream gcode;
- gcode << "G1 X" << XYZF_NUM(point(0))
- << " Y" << XYZF_NUM(point(1));
- if (! m_extrusion_axis.empty())
- // not gcfNoExtrusion
- gcode << " " << m_extrusion_axis << E_NUM(m_extruder->E());
- COMMENT(comment);
- gcode << "\n";
- return gcode.str();
+
+ G1Writer w;
+ w.emit_xy(point);
+ w.emit_e(m_extrusion_axis, m_extruder->E());
+ w.emit_comment(this->config.gcode_comments, comment);
+ return w.string();
}
std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment)
@@ -420,16 +487,11 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std
m_lifted = 0;
m_extruder->extrude(dE);
- std::ostringstream gcode;
- gcode << "G1 X" << XYZF_NUM(point(0))
- << " Y" << XYZF_NUM(point(1))
- << " Z" << XYZF_NUM(point(2));
- if (! m_extrusion_axis.empty())
- // not gcfNoExtrusion
- gcode << " " << m_extrusion_axis << E_NUM(m_extruder->E());
- COMMENT(comment);
- gcode << "\n";
- return gcode.str();
+ G1Writer w;
+ w.emit_xyz(point);
+ w.emit_e(m_extrusion_axis, m_extruder->E());
+ w.emit_comment(this->config.gcode_comments, comment);
+ return w.string();
}
std::string GCodeWriter::retract(bool before_wipe)
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index 6f9e4fd4c..fb6ab5635 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -161,8 +161,10 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
if (!result)
throw Slic3r::RuntimeError("Loading of a model file failed.");
+#if !ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
if (model.objects.empty())
throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty");
+#endif // !ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
for (ModelObject *o : model.objects)
{
@@ -557,6 +559,21 @@ std::string Model::propose_export_file_name_and_path(const std::string &new_exte
return boost::filesystem::path(this->propose_export_file_name_and_path()).replace_extension(new_extension).string();
}
+bool Model::is_fdm_support_painted() const
+{
+ return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_fdm_support_painted(); });
+}
+
+bool Model::is_seam_painted() const
+{
+ return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_seam_painted(); });
+}
+
+bool Model::is_mm_painted() const
+{
+ return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_mm_painted(); });
+}
+
ModelObject::~ModelObject()
{
this->clear_volumes();
@@ -733,6 +750,16 @@ void ModelObject::clear_volumes()
this->invalidate_bounding_box();
}
+bool ModelObject::is_fdm_support_painted() const
+{
+ return std::any_of(this->volumes.cbegin(), this->volumes.cend(), [](const ModelVolume *mv) { return mv->is_fdm_support_painted(); });
+}
+
+bool ModelObject::is_seam_painted() const
+{
+ return std::any_of(this->volumes.cbegin(), this->volumes.cend(), [](const ModelVolume *mv) { return mv->is_seam_painted(); });
+}
+
bool ModelObject::is_mm_painted() const
{
return std::any_of(this->volumes.cbegin(), this->volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index ec6fac821..ba3156139 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -285,6 +285,10 @@ public:
void clear_volumes();
void sort_volumes(bool full_sort);
bool is_multiparts() const { return volumes.size() > 1; }
+ // Checks if any of object volume is painted using the fdm support painting gizmo.
+ bool is_fdm_support_painted() const;
+ // Checks if any of object volume is painted using the seam painting gizmo.
+ bool is_seam_painted() const;
// Checks if any of object volume is painted using the multi-material painting gizmo.
bool is_mm_painted() const;
@@ -723,6 +727,8 @@ public:
this->mmu_segmentation_facets.set_new_unique_id();
}
+ bool is_fdm_support_painted() const { return !this->supported_facets.empty(); }
+ bool is_seam_painted() const { return !this->seam_facets.empty(); }
bool is_mm_painted() const { return !this->mmu_segmentation_facets.empty(); }
protected:
@@ -1127,6 +1133,13 @@ public:
// Propose an output path, replace extension. The new_extension shall contain the initial dot.
std::string propose_export_file_name_and_path(const std::string &new_extension) const;
+ // Checks if any of objects is painted using the fdm support painting gizmo.
+ bool is_fdm_support_painted() const;
+ // Checks if any of objects is painted using the seam painting gizmo.
+ bool is_seam_painted() const;
+ // Checks if any of objects is painted using the multi-material painting gizmo.
+ bool is_mm_painted() const;
+
private:
explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); }
void assign_new_unique_ids_recursive();
diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp
index a6be64299..68c40fc20 100644
--- a/src/libslic3r/Polyline.cpp
+++ b/src/libslic3r/Polyline.cpp
@@ -120,7 +120,8 @@ void Polyline::simplify(double tolerance)
this->points = MultiPoint::_douglas_peucker(this->points, tolerance);
}
-/* This method simplifies all *lines* contained in the supplied area */
+#if 0
+// This method simplifies all *lines* contained in the supplied area
template <class T>
void Polyline::simplify_by_visibility(const T &area)
{
@@ -141,6 +142,7 @@ void Polyline::simplify_by_visibility(const T &area)
}
template void Polyline::simplify_by_visibility<ExPolygon>(const ExPolygon &area);
template void Polyline::simplify_by_visibility<ExPolygonCollection>(const ExPolygonCollection &area);
+#endif
void Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
{
diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp
index 51dcf9d36..758fc38cd 100644
--- a/src/libslic3r/Polyline.hpp
+++ b/src/libslic3r/Polyline.hpp
@@ -72,7 +72,7 @@ public:
void extend_start(double distance);
Points equally_spaced_points(double distance) const;
void simplify(double tolerance);
- template <class T> void simplify_by_visibility(const T &area);
+// template <class T> void simplify_by_visibility(const T &area);
void split_at(const Point &point, Polyline* p1, Polyline* p2) const;
bool is_straight() const;
bool is_closed() const { return this->points.front() == this->points.back(); }
diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp
index 3883c4980..f5c8235ed 100644
--- a/src/libslic3r/Preset.cpp
+++ b/src/libslic3r/Preset.cpp
@@ -428,9 +428,9 @@ static std::vector<std::string> s_Preset_print_options {
#endif /* HAS_PRESSURE_EQUALIZER */
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
- "bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "perimeter_acceleration", "infill_acceleration",
- "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
- "min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
+ "bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "first_layer_speed_over_raft", "perimeter_acceleration", "infill_acceleration",
+ "bridge_acceleration", "first_layer_acceleration", "first_layer_acceleration_over_raft", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
+ "min_skirt_length", "brim_width", "brim_separation", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
"support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_closing_radius", "support_material_style",
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers",
diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp
index 9ac8864f9..cb9f04e45 100644
--- a/src/libslic3r/PresetBundle.cpp
+++ b/src/libslic3r/PresetBundle.cpp
@@ -240,7 +240,6 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward
if (! errors_cummulative.empty())
throw Slic3r::RuntimeError(errors_cummulative);
- // ysToDo : set prefered filament or sla_material (relates to print technology) and force o use of preffered printer model if it was added
this->load_selections(config, preferred_selection);
return substitutions;
@@ -466,20 +465,9 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
// will be selected by the following call of this->update_compatible(PresetSelectCompatibleType::Always).
const Preset *initial_printer = printers.find_preset(initial_printer_profile_name);
+ // If executed due to a Config Wizard update, preferred_printer contains the first newly installed printer, otherwise nullptr.
const Preset *preferred_printer = printers.find_system_preset_by_model_and_variant(preferred_selection.printer_model_id, preferred_selection.printer_variant);
- printers.select_preset_by_name(
- (preferred_printer != nullptr /*&& (initial_printer == nullptr || !initial_printer->is_visible)*/) ?
- preferred_printer->name :
- initial_printer_profile_name,
- true);
-
- // select preferred filament/sla_material profile if any exists and is visible
- if (!preferred_selection.filament.empty())
- if (auto it = filaments.find_preset_internal(preferred_selection.filament); it != filaments.end() && it->is_visible)
- initial_filament_profile_name = it->name;
- if (!preferred_selection.sla_material.empty())
- if (auto it = sla_materials.find_preset_internal(preferred_selection.sla_material); it != sla_materials.end() && it->is_visible)
- initial_sla_material_profile_name = it->name;
+ printers.select_preset_by_name(preferred_printer ? preferred_printer->name : initial_printer_profile_name, true);
// Selects the profile, leaves it to -1 if the initial profile name is empty or if it was not found.
prints.select_preset_by_name_strict(initial_print_profile_name);
@@ -507,6 +495,21 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
this->update_compatible(PresetSelectCompatibleType::Always);
this->update_multi_material_filament_presets();
+ if (initial_printer != nullptr && (preferred_printer == nullptr || initial_printer == preferred_printer)) {
+ // Don't run the following code, as we want to activate default filament / SLA material profiles when installing and selecting a new printer.
+ // Only run this code if just a filament / SLA material was installed by Config Wizard for an active Printer.
+ auto printer_technology = printers.get_selected_preset().printer_technology();
+ if (printer_technology == ptFFF && ! preferred_selection.filament.empty()) {
+ if (auto it = filaments.find_preset_internal(preferred_selection.filament); it != filaments.end() && it->is_visible) {
+ filaments.select_preset_by_name_strict(preferred_selection.filament);
+ this->filament_presets.front() = filaments.get_selected_preset_name();
+ }
+ } else if (printer_technology == ptSLA && ! preferred_selection.sla_material.empty()) {
+ if (auto it = sla_materials.find_preset_internal(preferred_selection.sla_material); it != sla_materials.end() && it->is_visible)
+ sla_materials.select_preset_by_name_strict(preferred_selection.sla_material);
+ }
+ }
+
// Parse the initial physical printer name.
std::string initial_physical_printer_name = remove_ini_suffix(config.get("presets", "physical_printer"));
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index 48737f830..06052a62f 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -88,7 +88,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"filament_cost",
"filament_spool_weight",
"first_layer_acceleration",
+ "first_layer_acceleration_over_raft",
"first_layer_bed_temperature",
+ "first_layer_speed_over_raft",
"gcode_comments",
"gcode_label_objects",
"infill_acceleration",
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index f010bad39..2917a9a19 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -505,10 +505,10 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimple;
def->set_default_value(new ConfigOptionEnum<BrimType>(btOuterOnly));
- def = this->add("brim_offset", coFloat);
- def->label = L("Brim offset");
+ def = this->add("brim_separation", coFloat);
+ def->label = L("Brim separation gap");
def->category = L("Skirt and brim");
- def->tooltip = L("The offset of the brim from the printed object. The offset is applied after the elephant foot compensation.");
+ def->tooltip = L("Offset of brim from the printed object. The offset is applied after the elephant foot compensation.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comAdvanced;
@@ -1152,6 +1152,15 @@ void PrintConfigDef::init_fff_params()
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(0));
+ def = this->add("first_layer_acceleration_over_raft", coFloat);
+ def->label = L("First object layer over raft interface");
+ def->tooltip = L("This is the acceleration your printer will use for first layer of object above raft interface. Set zero "
+ "to disable acceleration control for first layer of object above raft interface.");
+ def->sidetext = L("mm/s²");
+ def->min = 0;
+ def->mode = comExpert;
+ def->set_default_value(new ConfigOptionFloat(0));
+
def = this->add("first_layer_bed_temperature", coInts);
def->label = L("First layer");
def->full_label = L("First layer bed temperature");
@@ -1194,6 +1203,16 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(30, false));
+ def = this->add("first_layer_speed_over_raft", coFloatOrPercent);
+ def->label = L("Speed of object first layer over raft interface");
+ def->tooltip = L("If expressed as absolute value in mm/s, this speed will be applied to all the print moves "
+ "of the first object layer above raft interface, regardless of their type. If expressed as a percentage "
+ "(for example: 40%) it will scale the default speeds.");
+ def->sidetext = L("mm/s or %");
+ def->min = 0;
+ def->mode = comAdvanced;
+ def->set_default_value(new ConfigOptionFloatOrPercent(30, false));
+
def = this->add("first_layer_temperature", coInts);
def->label = L("First layer");
def->full_label = L("First layer nozzle temperature");
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 64c8445f8..d7409d12c 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -449,13 +449,15 @@ protected: \
PRINT_CONFIG_CLASS_DEFINE(
PrintObjectConfig,
- ((ConfigOptionFloat, brim_offset))
+ ((ConfigOptionFloat, brim_separation))
((ConfigOptionEnum<BrimType>, brim_type))
((ConfigOptionFloat, brim_width))
((ConfigOptionBool, clip_multipart_objects))
((ConfigOptionBool, dont_support_bridges))
((ConfigOptionFloat, elefant_foot_compensation))
((ConfigOptionFloatOrPercent, extrusion_width))
+ ((ConfigOptionFloat, first_layer_acceleration_over_raft))
+ ((ConfigOptionFloatOrPercent, first_layer_speed_over_raft))
((ConfigOptionBool, infill_only_where_needed))
// Force the generation of solid shells between adjacent materials/volumes.
((ConfigOptionBool, interface_shells))
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index d8d26baa6..d9a4f2670 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -500,7 +500,7 @@ bool PrintObject::invalidate_state_by_config_options(
bool invalidated = false;
for (const t_config_option_key &opt_key : opt_keys) {
if ( opt_key == "brim_width"
- || opt_key == "brim_offset"
+ || opt_key == "brim_separation"
|| opt_key == "brim_type") {
// Brim is printed below supports, support invalidates brim and skirt.
steps.emplace_back(posSupportMaterial);
@@ -2293,14 +2293,24 @@ void PrintObject::project_and_append_custom_facets(
const indexed_triangle_set custom_facets = seam
? mv->seam_facets.get_facets_strict(*mv, type)
: mv->supported_facets.get_facets_strict(*mv, type);
- if (! custom_facets.indices.empty())
-#if 0
- project_triangles_to_slabs(this->layers(), custom_facets,
- (this->trafo_centered() * mv->get_matrix()).cast<float>(),
- seam, out);
-#else
- slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &out, [](){});
-#endif
+ if (! custom_facets.indices.empty()) {
+ if (seam)
+ project_triangles_to_slabs(this->layers(), custom_facets,
+ (this->trafo_centered() * mv->get_matrix()).cast<float>(),
+ seam, out);
+ else {
+ std::vector<Polygons> projected;
+ slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){});
+ // Merge these projections with the output, layer by layer.
+ assert(! projected.empty());
+ assert(out.empty() || out.size() == projected.size());
+ if (out.empty())
+ out = std::move(projected);
+ else
+ for (size_t i = 0; i < out.size(); ++ i)
+ append(out[i], std::move(projected[i]));
+ }
+ }
}
}
diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp
index 6efc5f4a9..7efe15bb2 100644
--- a/src/libslic3r/QuadricEdgeCollapse.cpp
+++ b/src/libslic3r/QuadricEdgeCollapse.cpp
@@ -7,6 +7,10 @@
using namespace Slic3r;
+#ifndef NDEBUG
+// #define EXPENSIVE_DEBUG_CHECKS
+#endif // NDEBUG
+
// only private namespace not neccessary be in .hpp
namespace QuadricEdgeCollapse {
using Vertices = std::vector<stl_vertex>;
@@ -79,10 +83,13 @@ namespace QuadricEdgeCollapse {
init(const indexed_triangle_set &its, ThrowOnCancel& throw_on_cancel, StatusFn& status_fn);
std::optional<uint32_t> find_triangle_index1(uint32_t vi, const VertexInfo& v_info,
uint32_t ti, const EdgeInfos& e_infos, const Indices& indices);
+ void reorder_edges(EdgeInfos &e_infos, const VertexInfo &v_info, uint32_t ti0, uint32_t ti1);
bool is_flipped(const Vec3f &new_vertex, uint32_t ti0, uint32_t ti1, const VertexInfo& v_info,
const TriangleInfos &t_infos, const EdgeInfos &e_infos, const indexed_triangle_set &its);
bool degenerate(uint32_t vi, uint32_t ti0, uint32_t ti1, const VertexInfo &v_info,
const EdgeInfos &e_infos, const Indices &indices);
+ bool create_no_volume(uint32_t vi0, uint32_t vi1, uint32_t ti0, uint32_t ti1,
+ const VertexInfo &v_info0, const VertexInfo &v_info1, const EdgeInfos &e_infos, const Indices &indices);
// find edge with smallest error in triangle
Vec3d calculate_3errors(const Triangle &t, const Vertices &vertices, const VertexInfos &v_infos);
Error calculate_error(uint32_t ti, const Triangle& t,const Vertices &vertices, const VertexInfos& v_infos, unsigned char& min_index);
@@ -92,14 +99,25 @@ namespace QuadricEdgeCollapse {
const Triangle &t1, CopyEdgeInfos& infos, EdgeInfos &e_infos1);
void compact(const VertexInfos &v_infos, const TriangleInfos &t_infos, const EdgeInfos &e_infos, indexed_triangle_set &its);
-#ifndef NDEBUG
+#ifdef EXPENSIVE_DEBUG_CHECKS
void store_surround(const char *obj_filename, size_t triangle_index, int depth, const indexed_triangle_set &its,
const VertexInfos &v_infos, const EdgeInfos &e_infos);
bool check_neighbors(const indexed_triangle_set &its, const TriangleInfos &t_infos,
const VertexInfos &v_infos, const EdgeInfos &e_infos);
-#endif /* NDEBUG */
+#endif /* EXPENSIVE_DEBUG_CHECKS */
-} // namespace QuadricEdgeCollapse
+ // constants --> may be move to config
+ const uint32_t check_cancel_period = 16; // how many edge to reduce before call throw_on_cancel
+ const size_t max_triangle_count_for_one_vertex = 50;
+ // change speed of progress bargraph
+ const int status_init_size = 10; // in percents
+ // parts of init size
+ const int status_normal_size = 25;
+ const int status_sum_quadric = 25;
+ const int status_set_offsets = 10;
+ const int status_calc_errors = 30;
+ const int status_create_refs = 10;
+ } // namespace QuadricEdgeCollapse
using namespace QuadricEdgeCollapse;
@@ -110,10 +128,6 @@ void Slic3r::its_quadric_edge_collapse(
std::function<void(void)> throw_on_cancel,
std::function<void(int)> status_fn)
{
- // constants --> may be move to config
- const int status_init_size = 10; // in percents
- const int check_cancel_period = 16; // how many edge to reduce before call throw_on_cancel
-
// check input
if (triangle_count >= its.indices.size()) return;
float maximal_error = (max_error == nullptr)? std::numeric_limits<float>::max() : *max_error;
@@ -122,7 +136,8 @@ void Slic3r::its_quadric_edge_collapse(
if (status_fn == nullptr) status_fn = [](int) {};
StatusFn init_status_fn = [&](int percent) {
- status_fn(std::round((percent * status_init_size) / 100.));
+ float n_percent = percent * status_init_size / 100.f;
+ status_fn(static_cast<int>(std::round(n_percent)));
};
TriangleInfos t_infos; // only normals with information about deleted triangle
@@ -145,7 +160,6 @@ void Slic3r::its_quadric_edge_collapse(
mpq.reserve(its.indices.size());
for (Error &error :errors) mpq.push(error);
- const size_t max_triangle_count_for_one_vertex = 50;
CopyEdgeInfos ceis;
ceis.reserve(max_triangle_count_for_one_vertex);
EdgeInfos e_infos_swap;
@@ -162,8 +176,9 @@ void Slic3r::its_quadric_edge_collapse(
(1. - reduced);
status_fn(static_cast<int>(std::round(status)));
};
- // modulo for update status
- uint32_t status_mod = std::max(uint32_t(16), count_triangle_to_reduce / 100);
+ // modulo for update status, call each percent only once
+ uint32_t status_mod = std::max(uint32_t(16),
+ count_triangle_to_reduce / (100 - status_init_size));
uint32_t iteration_number = 0;
float last_collapsed_error = 0.f;
@@ -195,14 +210,21 @@ void Slic3r::its_quadric_edge_collapse(
q += v_info1.q;
Vec3f new_vertex0 = calculate_vertex(vi0, vi1, q, its.vertices);
// set of triangle indices that change quadric
+ uint32_t ti1 = -1; // triangle 1 index
auto ti1_opt = (v_info0.count < v_info1.count)?
find_triangle_index1(vi1, v_info0, ti0, e_infos, its.indices) :
find_triangle_index1(vi0, v_info1, ti0, e_infos, its.indices) ;
+ if (ti1_opt.has_value()) {
+ ti1 = *ti1_opt;
+ reorder_edges(e_infos, v_info0, ti0, ti1);
+ reorder_edges(e_infos, v_info1, ti0, ti1);
+ }
if (!ti1_opt.has_value() || // edge has only one triangle
- degenerate(vi0, ti0, *ti1_opt, v_info1, e_infos, its.indices) ||
- degenerate(vi1, ti0, *ti1_opt, v_info0, e_infos, its.indices) ||
- is_flipped(new_vertex0, ti0, *ti1_opt, v_info0, t_infos, e_infos, its) ||
- is_flipped(new_vertex0, ti0, *ti1_opt, v_info1, t_infos, e_infos, its)) {
+ degenerate(vi0, ti0, ti1, v_info1, e_infos, its.indices) ||
+ degenerate(vi1, ti0, ti1, v_info0, e_infos, its.indices) ||
+ create_no_volume(vi0, vi1, ti0, ti1, v_info0, v_info1, e_infos, its.indices) ||
+ is_flipped(new_vertex0, ti0, ti1, v_info0, t_infos, e_infos, its) ||
+ is_flipped(new_vertex0, ti0, ti1, v_info1, t_infos, e_infos, its)) {
// try other triangle's edge
Vec3d errors = calculate_3errors(t0, its.vertices, v_infos);
Vec3i ord = (errors[0] < errors[1]) ?
@@ -227,29 +249,25 @@ void Slic3r::its_quadric_edge_collapse(
mpq.push(e);
continue;
}
- uint32_t ti1 = *ti1_opt;
+
last_collapsed_error = e.value;
changed_triangle_indices.clear();
changed_triangle_indices.reserve(v_info0.count + v_info1.count - 4);
// for each vertex0 triangles
- uint32_t v_info0_end = v_info0.start + v_info0.count;
+ uint32_t v_info0_end = v_info0.start + v_info0.count - 2;
for (uint32_t di = v_info0.start; di < v_info0_end; ++di) {
assert(di < e_infos.size());
uint32_t ti = e_infos[di].t_index;
- if (ti == ti0) continue; // ti0 will be deleted
- if (ti == ti1) continue; // ti1 will be deleted
changed_triangle_indices.emplace_back(ti);
}
// for each vertex1 triangles
- uint32_t v_info1_end = v_info1.start + v_info1.count;
+ uint32_t v_info1_end = v_info1.start + v_info1.count - 2;
for (uint32_t di = v_info1.start; di < v_info1_end; ++di) {
assert(di < e_infos.size());
EdgeInfo &e_info = e_infos[di];
uint32_t ti = e_info.t_index;
- if (ti == ti0) continue; // ti0 will be deleted
- if (ti == ti1) continue; // ti1 will be deleted
Triangle &t = its.indices[ti];
t[e_info.edge] = vi0; // change index
changed_triangle_indices.emplace_back(ti);
@@ -282,7 +300,9 @@ void Slic3r::its_quadric_edge_collapse(
t_info1.set_deleted();
// triangle counter decrementation
actual_triangle_count-=2;
+#ifdef EXPENSIVE_DEBUG_CHECKS
assert(check_neighbors(its, t_infos, v_infos, e_infos));
+#endif // EXPENSIVE_DEBUG_CHECKS
}
// compact triangle
@@ -384,13 +404,6 @@ SymMat QuadricEdgeCollapse::create_quadric(const Triangle &t,
std::tuple<TriangleInfos, VertexInfos, EdgeInfos, Errors>
QuadricEdgeCollapse::init(const indexed_triangle_set &its, ThrowOnCancel& throw_on_cancel, StatusFn& status_fn)
{
- // change speed of progress bargraph
- const int status_normal_size = 25;
- const int status_sum_quadric = 25;
- const int status_set_offsets = 10;
- const int status_calc_errors = 30;
- const int status_create_refs = 10;
-
int status_offset = 0;
TriangleInfos t_infos(its.indices.size());
VertexInfos v_infos(its.vertices.size());
@@ -506,6 +519,38 @@ std::optional<uint32_t> QuadricEdgeCollapse::find_triangle_index1(uint32_t
return {};
}
+void QuadricEdgeCollapse::reorder_edges(EdgeInfos & e_infos,
+ const VertexInfo &v_info,
+ uint32_t ti0,
+ uint32_t ti1)
+{
+ // swap edge info of ti0 and ti1 to end(last one and one before)
+ size_t v_info_end = v_info.start + v_info.count - 2;
+ EdgeInfo &e_info_ti0 = e_infos[v_info_end];
+ EdgeInfo &e_info_ti1 = e_infos[v_info_end+1];
+ bool is_swaped = false;
+ for (size_t ei = v_info.start; ei < v_info_end; ++ei) {
+ EdgeInfo &e_info = e_infos[ei];
+ if (e_info.t_index == ti0) {
+ std::swap(e_info, e_info_ti0);
+ if (is_swaped) return;
+ if (e_info.t_index == ti1) {
+ std::swap(e_info, e_info_ti1);
+ return;
+ }
+ is_swaped = true;
+ } else if (e_info.t_index == ti1) {
+ std::swap(e_info, e_info_ti1);
+ if (is_swaped) return;
+ if (e_info.t_index == ti0) {
+ std::swap(e_info, e_info_ti0);
+ return;
+ }
+ is_swaped = true;
+ }
+ }
+}
+
bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex,
uint32_t ti0,
uint32_t ti1,
@@ -519,12 +564,10 @@ bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex,
static const float dot_thr = 0.2f; // Value from simplify mesh cca 80 DEG
// for each vertex triangles
- size_t v_info_end = v_info.start + v_info.count;
+ size_t v_info_end = v_info.start + v_info.count-2;
for (size_t ei = v_info.start; ei < v_info_end; ++ei) {
assert(ei < e_infos.size());
const EdgeInfo &e_info = e_infos[ei];
- if (e_info.t_index == ti0) continue; // ti0 will be deleted
- if (e_info.t_index == ti1) continue; // ti1 will be deleted
const Triangle &t = its.indices[e_info.t_index];
const Vec3f &normal = t_infos[e_info.t_index].n;
const Vec3f &vf = its.vertices[t[(e_info.edge + 1) % 3]];
@@ -554,12 +597,10 @@ bool QuadricEdgeCollapse::degenerate(uint32_t vi,
{
// check surround triangle do not contain vertex index
// protect from creation of triangle with two same vertices inside
- size_t v_info_end = v_info.start + v_info.count;
+ size_t v_info_end = v_info.start + v_info.count - 2;
for (size_t ei = v_info.start; ei < v_info_end; ++ei) {
assert(ei < e_infos.size());
const EdgeInfo &e_info = e_infos[ei];
- if (e_info.t_index == ti0) continue; // ti0 will be deleted
- if (e_info.t_index == ti1) continue; // ti1 will be deleted
const Triangle &t = indices[e_info.t_index];
for (size_t i = 0; i < 3; ++i)
if (static_cast<uint32_t>(t[i]) == vi) return true;
@@ -567,6 +608,52 @@ bool QuadricEdgeCollapse::degenerate(uint32_t vi,
return false;
}
+bool QuadricEdgeCollapse::create_no_volume(
+ uint32_t vi0 , uint32_t vi1,
+ uint32_t ti0 , uint32_t ti1,
+ const VertexInfo &v_info0, const VertexInfo &v_info1,
+ const EdgeInfos & e_infos, const Indices &indices)
+{
+ // check that triangles around vertex0 doesn't have half edge
+ // with opposit order in set of triangles around vertex1
+ // protect from creation of two triangles with oposit order - no volume space
+ size_t v_info0_end = v_info0.start + v_info0.count - 2;
+ size_t v_info1_end = v_info1.start + v_info1.count - 2;
+ for (size_t ei0 = v_info0.start; ei0 < v_info0_end; ++ei0) {
+ const EdgeInfo &e_info0 = e_infos[ei0];
+ const Triangle &t0 = indices[e_info0.t_index];
+ // edge CCW vertex indices are t0vi0, t0vi1
+ size_t t0i = 0;
+ uint32_t t0vi0 = static_cast<uint32_t>(t0[t0i]);
+ if (t0vi0 == vi0) {
+ ++t0i;
+ t0vi0 = static_cast<uint32_t>(t0[t0i]);
+ }
+ ++t0i;
+ uint32_t t0vi1 = static_cast<uint32_t>(t0[t0i]);
+ if (t0vi1 == vi0) {
+ ++t0i;
+ t0vi1 = static_cast<uint32_t>(t0[t0i]);
+ }
+ for (size_t ei1 = v_info1.start; ei1 < v_info1_end; ++ei1) {
+ const EdgeInfo &e_info1 = e_infos[ei1];
+ const Triangle &t1 = indices[e_info1.t_index];
+ size_t t1i = 0;
+ for (; t1i < 3; ++t1i) if (static_cast<uint32_t>(t1[t1i]) == t0vi1) break;
+ if (t1i >= 3) continue; // without vertex index from triangle 0
+ // check if second index is same too
+ ++t1i;
+ if (t1i == 3) t1i = 0; // triangle loop(modulo 3)
+ if (static_cast<uint32_t>(t1[t1i]) == vi1) {
+ ++t1i;
+ if (t1i == 3) t1i = 0; // triangle loop(modulo 3)
+ }
+ if (static_cast<uint32_t>(t1[t1i]) == t0vi0) return true;
+ }
+ }
+ return false;
+}
+
Vec3d QuadricEdgeCollapse::calculate_3errors(const Triangle & t,
const Vertices & vertices,
const VertexInfos &v_infos)
@@ -732,7 +819,8 @@ void QuadricEdgeCollapse::compact(const VertexInfos & v_infos,
its.indices.erase(its.indices.begin() + ti_new, its.indices.end());
}
-#ifndef NDEBUG
+#ifdef EXPENSIVE_DEBUG_CHECKS
+
// store triangle surrounding to file
void QuadricEdgeCollapse::store_surround(const char *obj_filename,
size_t triangle_index,
@@ -842,4 +930,4 @@ bool QuadricEdgeCollapse::check_neighbors(const indexed_triangle_set &its,
}
return true;
}
-#endif /* NDEBUG */
+#endif /* EXPENSIVE_DEBUG_CHECKS */
diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp
index 2e4a47954..1b66bcc53 100644
--- a/src/libslic3r/SupportMaterial.cpp
+++ b/src/libslic3r/SupportMaterial.cpp
@@ -2810,22 +2810,22 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
Polygons brim;
if (object.has_brim()) {
// Calculate the area covered by the brim.
- const BrimType brim_type = object.config().brim_type;
- const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner;
- const bool brim_inner = brim_type == btInnerOnly || brim_type == btOuterAndInner;
- const auto brim_offset = scaled<float>(object.config().brim_offset.value + object.config().brim_width.value);
+ const BrimType brim_type = object.config().brim_type;
+ const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner;
+ const bool brim_inner = brim_type == btInnerOnly || brim_type == btOuterAndInner;
+ const auto brim_separation = scaled<float>(object.config().brim_separation.value + object.config().brim_width.value);
for (const ExPolygon &ex : object.layers().front()->lslices) {
if (brim_outer && brim_inner)
- polygons_append(brim, offset(ex, brim_offset));
+ polygons_append(brim, offset(ex, brim_separation));
else {
if (brim_outer)
- polygons_append(brim, offset(ex.contour, brim_offset, ClipperLib::jtRound, float(scale_(0.1))));
+ polygons_append(brim, offset(ex.contour, brim_separation, ClipperLib::jtRound, float(scale_(0.1))));
else
brim.emplace_back(ex.contour);
if (brim_inner) {
Polygons holes = ex.holes;
polygons_reverse(holes);
- holes = offset(holes, - brim_offset, ClipperLib::jtRound, float(scale_(0.1)));
+ holes = offset(holes, - brim_separation, ClipperLib::jtRound, float(scale_(0.1)));
polygons_reverse(holes);
polygons_append(brim, std::move(holes));
} else
diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 6e1d6cdd7..3305822a4 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -37,24 +37,37 @@
//====================
-// 2.4.0.alpha0 techs
+// 2.4.0.alpha1 techs
//====================
-#define ENABLE_2_4_0_ALPHA0 1
+#define ENABLE_2_4_0_ALPHA1 1
// Enable delayed rendering of transparent volumes
-#define ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_2_4_0_ALPHA0)
+#define ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_2_4_0_ALPHA1)
// Enable the fix of importing color print view from gcode files into GCodeViewer
-#define ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER (1 && ENABLE_2_4_0_ALPHA0)
+#define ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER (1 && ENABLE_2_4_0_ALPHA1)
// Enable drawing contours, at cut level, for sinking volumes
-#define ENABLE_SINKING_CONTOURS (1 && ENABLE_2_4_0_ALPHA0)
+#define ENABLE_SINKING_CONTOURS (1 && ENABLE_2_4_0_ALPHA1)
// Enable implementation of retract acceleration in gcode processor
-#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA0)
+#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA1)
// Enable the fix for exporting and importing to/from 3mf file of mirrored volumes
-#define ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT (1 && ENABLE_2_4_0_ALPHA0)
+#define ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT (1 && ENABLE_2_4_0_ALPHA1)
// Enable rendering seams (and other options) in preview using models
-#define ENABLE_SEAMS_USING_MODELS (1 && ENABLE_2_4_0_ALPHA0)
+#define ENABLE_SEAMS_USING_MODELS (1 && ENABLE_2_4_0_ALPHA1)
+// Enable save and save as commands to be enabled also when the plater is empty and allow to load empty projects
+#define ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED (1 && ENABLE_2_4_0_ALPHA1)
+
+
+//====================
+// 2.4.0.alpha2 techs
+//====================
+#define ENABLE_2_4_0_ALPHA2 1
+
+// Enable rendering seams (and other options) in preview using batched models on systems not supporting OpenGL 3.3
+#define ENABLE_SEAMS_USING_BATCHED_MODELS (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_2_4_0_ALPHA2)
+// Enable fixing the z position of color change, pause print and custom gcode markers in preview
+#define ENABLE_FIX_PREVIEW_OPTIONS_Z (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER && ENABLE_2_4_0_ALPHA2)
// Enable replacing a missing file during reload from disk command
-#define ENABLE_RELOAD_FROM_DISK_REPLACE_FILE (1 && ENABLE_2_4_0_ALPHA0)
+#define ENABLE_RELOAD_FROM_DISK_REPLACE_FILE (1 && ENABLE_2_4_0_ALPHA2)
#endif // _prusaslicer_technologies_h_
diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp
index c330f34b2..c5dbdac9c 100644
--- a/src/libslic3r/utils.cpp
+++ b/src/libslic3r/utils.cpp
@@ -984,16 +984,11 @@ std::string log_memory_info(bool ignore_loglevel)
} PROCESS_MEMORY_COUNTERS_EX, *PPROCESS_MEMORY_COUNTERS_EX;
#endif /* PROCESS_MEMORY_COUNTERS_EX */
-
- HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ::GetCurrentProcessId());
- if (hProcess != nullptr) {
- PROCESS_MEMORY_COUNTERS_EX pmc;
- if (GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)))
- out = " WorkingSet: " + format_memsize_MB(pmc.WorkingSetSize) + "; PrivateBytes: " + format_memsize_MB(pmc.PrivateUsage) + "; Pagefile(peak): " + format_memsize_MB(pmc.PagefileUsage) + "(" + format_memsize_MB(pmc.PeakPagefileUsage) + ")";
- else
- out += " Used memory: N/A";
- CloseHandle(hProcess);
- }
+ PROCESS_MEMORY_COUNTERS_EX pmc;
+ if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)))
+ out = " WorkingSet: " + format_memsize_MB(pmc.WorkingSetSize) + "; PrivateBytes: " + format_memsize_MB(pmc.PrivateUsage) + "; Pagefile(peak): " + format_memsize_MB(pmc.PagefileUsage) + "(" + format_memsize_MB(pmc.PeakPagefileUsage) + ")";
+ else
+ out += " Used memory: N/A";
#elif defined(__linux__) or defined(__APPLE__)
// Get current memory usage.
#ifdef __APPLE__