diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index b00f85ba70..d5edcb5719 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -20,8 +20,8 @@
# therefore, unfortunatelly, the installation cannot be copied/moved elsewhere without re-installing wxWidgets.
#
+cmake_minimum_required(VERSION 3.10)
project(PrusaSlicer-deps)
-cmake_minimum_required(VERSION 3.2)
include(ExternalProject)
include(ProcessorCount)
@@ -62,6 +62,10 @@ if (NOT _is_multi AND NOT CMAKE_BUILD_TYPE)
message(STATUS "Forcing CMAKE_BUILD_TYPE to Release as it was not specified.")
endif ()
+if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.24)
+ cmake_policy(SET CMP0135 NEW)
+endif ()
+
function(prusaslicer_add_cmake_project projectname)
cmake_parse_arguments(P_ARGS "" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN})
@@ -195,6 +199,8 @@ include(NanoSVG/NanoSVG.cmake)
include(wxWidgets/wxWidgets.cmake)
include(OCCT/OCCT.cmake)
+include(LibBGCode/LibBGCode.cmake)
+
set(_dep_list
dep_Boost
dep_TBB
@@ -210,6 +216,7 @@ set(_dep_list
${PNG_PKG}
${ZLIB_PKG}
${EXPAT_PKG}
+ dep_LibBGCode
)
# if (NOT MSVC)
diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake
new file mode 100644
index 0000000000..076fe290a2
--- /dev/null
+++ b/deps/LibBGCode/LibBGCode.cmake
@@ -0,0 +1,34 @@
+set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory")
+
+set(_source_dir_line
+ URL https://github.com/prusa3d/libbgcode/archive/49e7b47b7a9a7e9365613d3f90ad49d71a0e93d6.zip
+ URL_HASH SHA256=b9513ab2bbaf06a79c35c4d69226d211f1226ca625ce07c04f54b89b93e9bc75
+)
+
+if (LibBGCode_SOURCE_DIR)
+ set(_source_dir_line "SOURCE_DIR;${LibBGCode_SOURCE_DIR};BUILD_ALWAYS;ON")
+endif ()
+
+prusaslicer_add_cmake_project(LibBGCode_deps
+ ${_source_dir_line}
+ SOURCE_SUBDIR deps
+ DEPENDS dep_Boost ${ZLIB_PKG}
+ CMAKE_ARGS
+ -DDEP_DOWNLOAD_DIR:PATH=${DEP_DOWNLOAD_DIR}
+ -DDEP_CMAKE_OPTS:STRING=-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON
+ -DLibBGCode_Deps_SELECT_ALL:BOOL=OFF
+ -DLibBGCode_Deps_SELECT_heatshrink:BOOL=ON
+ -DDESTDIR=${DESTDIR}
+)
+
+prusaslicer_add_cmake_project(LibBGCode
+ ${_source_dir_line}
+ DEPENDS dep_LibBGCode_deps
+ CMAKE_ARGS
+ -DLibBGCode_BUILD_TESTS:BOOL=OFF
+ -DLibBGCode_BUILD_CMD_TOOL:BOOL=OFF
+)
+
+if (MSVC)
+ add_debug_dep(dep_LibBGCode)
+endif ()
\ No newline at end of file
diff --git a/resources/icons/convert_file.svg b/resources/icons/convert_file.svg
new file mode 100644
index 0000000000..2de2b707f0
--- /dev/null
+++ b/resources/icons/convert_file.svg
@@ -0,0 +1,93 @@
+
+
+
+
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 41ef6482aa..95728d63a7 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -14,6 +14,11 @@ if (TARGET OpenVDB::openvdb)
set(OpenVDBUtils_SOURCES OpenVDBUtils.cpp OpenVDBUtils.hpp OpenVDBUtilsLegacy.hpp)
endif()
+find_package(LibBGCode REQUIRED COMPONENTS Convert)
+slic3r_remap_configs(LibBGCode::bgcode_core RelWithDebInfo Release)
+slic3r_remap_configs(LibBGCode::bgcode_binarize RelWithDebInfo Release)
+slic3r_remap_configs(LibBGCode::bgcode_convert RelWithDebInfo Release)
+
set(SLIC3R_SOURCES
pchheader.cpp
pchheader.hpp
@@ -571,6 +576,7 @@ target_link_libraries(libslic3r
ZLIB::ZLIB
JPEG::JPEG
qoi
+ LibBGCode::bgcode_convert
)
if (APPLE)
diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp
index 28224d9baf..b6b967dba6 100644
--- a/src/libslic3r/Config.cpp
+++ b/src/libslic3r/Config.cpp
@@ -23,6 +23,10 @@
#include
#include
+#if ENABLE_BINARIZED_GCODE
+#include
+#endif // ENABLE_BINARIZED_GCODE
+
//FIXME for GCodeFlavor and gcfMarlin (for forward-compatibility conversion)
// This is not nice, likely it would be better to pass the ConfigSubstitutionContext to handle_legacy().
#include "PrintConfig.hpp"
@@ -720,11 +724,43 @@ void ConfigBase::setenv_() const
}
}
-ConfigSubstitutions ConfigBase::load(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule)
+ConfigSubstitutions ConfigBase::load(const std::string& filename, ForwardCompatibilitySubstitutionRule compatibility_rule)
{
- return is_gcode_file(file) ?
- this->load_from_gcode_file(file, compatibility_rule) :
- this->load_from_ini(file, compatibility_rule);
+#if ENABLE_BINARIZED_GCODE
+ enum class EFileType
+ {
+ Ini,
+ AsciiGCode,
+ BinaryGCode
+ };
+
+ EFileType file_type;
+
+ if (is_gcode_file(filename)) {
+ FILE* file = boost::nowide::fopen(filename.c_str(), "rb");
+ if (file == nullptr)
+ throw Slic3r::RuntimeError("Error opening the file: " + filename + "\n");
+
+ using namespace bgcode::core;
+ std::vector cs_buffer(65536);
+ file_type = (is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == EResult::Success) ? EFileType::BinaryGCode : EFileType::AsciiGCode;
+ fclose(file);
+ }
+ else
+ file_type = EFileType::Ini;
+
+ switch (file_type)
+ {
+ case EFileType::Ini: { return this->load_from_ini(filename, compatibility_rule); }
+ case EFileType::AsciiGCode: { return this->load_from_gcode_file(filename, compatibility_rule);}
+ case EFileType::BinaryGCode: { return this->load_from_binary_gcode_file(filename, compatibility_rule);}
+ default: { throw Slic3r::RuntimeError("Invalid file: " + filename + "\n"); }
+ }
+#else
+ return is_gcode_file(filename) ?
+ this->load_from_gcode_file(filename, compatibility_rule) :
+ this->load_from_ini(filename, compatibility_rule);
+#endif // ENABLE_BINARIZED_GCODE
}
ConfigSubstitutions ConfigBase::load_from_ini(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule)
@@ -928,15 +964,15 @@ private:
};
// Load the config keys from the tail of a G-code file.
-ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule)
+ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &filename, ForwardCompatibilitySubstitutionRule compatibility_rule)
{
// Read a 64k block from the end of the G-code.
- boost::nowide::ifstream ifs(file, std::ifstream::binary);
+ boost::nowide::ifstream ifs(filename, std::ifstream::binary);
// Look for Slic3r or PrusaSlicer header.
// Look for the header across the whole file as the G-code may have been extended at the start by a post-processing script or the user.
bool has_delimiters = false;
{
- static constexpr const char slic3r_gcode_header[] = "; generated by Slic3r ";
+ static constexpr const char slic3r_gcode_header[] = "; generated by Slic3r ";
static constexpr const char prusaslicer_gcode_header[] = "; generated by PrusaSlicer ";
std::string header;
bool header_found = false;
@@ -986,7 +1022,7 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo
break;
}
if (! end_found)
- throw Slic3r::RuntimeError(format("Configuration block closing tag \"; prusaslicer_config = end\" not found when reading %1%", file));
+ throw Slic3r::RuntimeError(format("Configuration block closing tag \"; prusaslicer_config = end\" not found when reading %1%", filename));
std::string key, value;
while (reader.getline(line)) {
if (line == "; prusaslicer_config = begin") {
@@ -1009,7 +1045,7 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo
}
}
if (! begin_found)
- throw Slic3r::RuntimeError(format("Configuration block opening tag \"; prusaslicer_config = begin\" not found when reading %1%", file));
+ throw Slic3r::RuntimeError(format("Configuration block opening tag \"; prusaslicer_config = begin\" not found when reading %1%", filename));
}
else
{
@@ -1017,8 +1053,8 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo
// Try a heuristics reading the G-code from back.
ifs.seekg(0, ifs.end);
auto file_length = ifs.tellg();
- auto data_length = std::min(65535, file_length - header_end_pos);
- ifs.seekg(file_length - data_length, ifs.beg);
+ auto data_length = std::min(65535, file_length - header_end_pos);
+ ifs.seekg(file_length - data_length, ifs.beg);
std::vector data(size_t(data_length) + 1, 0);
ifs.read(data.data(), data_length);
ifs.close();
@@ -1026,10 +1062,52 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo
}
if (key_value_pairs < 80)
- throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs));
+ throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", filename, key_value_pairs));
return std::move(substitutions_ctxt.substitutions);
}
+#if ENABLE_BINARIZED_GCODE
+ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& filename, ForwardCompatibilitySubstitutionRule compatibility_rule)
+{
+ ConfigSubstitutionContext substitutions_ctxt(compatibility_rule);
+
+ FilePtr file{ boost::nowide::fopen(filename.c_str(), "rb") };
+ if (file.f == nullptr)
+ throw Slic3r::RuntimeError(format("Error opening the file: %1%", filename));
+
+ using namespace bgcode::core;
+ using namespace bgcode::binarize;
+ std::vector cs_buffer(65536);
+ EResult res = is_valid_binary_gcode(*file.f, true, cs_buffer.data(), cs_buffer.size());
+ if (res != EResult::Success)
+ throw Slic3r::RuntimeError(format("The selected file is not a valid binary gcode.\nError: %1%", std::string(translate_result(res))));
+
+ FileHeader file_header;
+ res = read_header(*file.f, file_header, nullptr);
+ if (res != EResult::Success)
+ throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res))));
+
+ // searches for config block
+ BlockHeader block_header;
+ res = read_next_block_header(*file.f, file_header, block_header, EBlockType::SlicerMetadata, cs_buffer.data(), cs_buffer.size());
+ if (res != EResult::Success)
+ throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res))));
+ if ((EBlockType)block_header.type != EBlockType::SlicerMetadata)
+ throw Slic3r::RuntimeError(format("Unable to find slicer metadata block in file: '%1%'", filename));
+ SlicerMetadataBlock slicer_metadata_block;
+ res = slicer_metadata_block.read_data(*file.f, file_header, block_header);
+ if (res != EResult::Success)
+ throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res))));
+
+ // extracts data from block
+ for (const auto& [key, value] : slicer_metadata_block.raw_data) {
+ this->set_deserialize(key, value, substitutions_ctxt);
+ }
+
+ return std::move(substitutions_ctxt.substitutions);
+}
+#endif // ENABLE_BINARIZED_GCODE
+
void ConfigBase::save(const std::string &file) const
{
boost::nowide::ofstream c;
diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp
index c0ab5266f7..16469f6947 100644
--- a/src/libslic3r/Config.hpp
+++ b/src/libslic3r/Config.hpp
@@ -2307,7 +2307,10 @@ public:
// Loading a "will be one day a legacy format" of configuration stored into 3MF or AMF.
// Accepts the same data as load_from_ini_string(), only with each configuration line possibly prefixed with a semicolon (G-code comment).
ConfigSubstitutions load_from_ini_string_commented(std::string &&data, ForwardCompatibilitySubstitutionRule compatibility_rule);
- ConfigSubstitutions load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule);
+ ConfigSubstitutions load_from_gcode_file(const std::string &filename, ForwardCompatibilitySubstitutionRule compatibility_rule);
+#if ENABLE_BINARIZED_GCODE
+ ConfigSubstitutions load_from_binary_gcode_file(const std::string& filename, ForwardCompatibilitySubstitutionRule compatibility_rule);
+#endif // ENABLE_BINARIZED_GCODE
ConfigSubstitutions load(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule);
void save(const std::string &file) const;
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 971ee8df6a..116077ba4c 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -20,7 +20,10 @@
#include "ClipperUtils.hpp"
#include "libslic3r.h"
#include "LocalesUtils.hpp"
-#include "libslic3r/format.hpp"
+#include "format.hpp"
+#if ENABLE_BINARIZED_GCODE
+#include "libslic3r_version.h"
+#endif // ENABLE_BINARIZED_GCODE
#include
#include
@@ -556,6 +559,9 @@ void GCodeGenerator::do_export(Print* print, const char* path, GCodeProcessorRes
m_processor.initialize(path_tmp);
m_processor.set_print(print);
+#if ENABLE_BINARIZED_GCODE
+ m_processor.get_binary_data() = bgcode::binarize::BinaryData();
+#endif // ENABLE_BINARIZED_GCODE
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");
@@ -688,68 +694,105 @@ namespace DoExport {
}
// Fill in print_statistics and return formatted string containing filament statistics to be inserted into G-code comment section.
+#if ENABLE_BINARIZED_GCODE
static std::string update_print_stats_and_format_filament_stats(
const bool has_wipe_tower,
- const WipeTowerData &wipe_tower_data,
+ const WipeTowerData &wipe_tower_data,
+ const FullPrintConfig &config,
+ const std::vector &extruders,
+ unsigned int initial_extruder_id,
+ PrintStatistics &print_statistics,
+ bool export_binary_data,
+ bgcode::binarize::BinaryData &binary_data)
+#else
+ static std::string update_print_stats_and_format_filament_stats(
+ const bool has_wipe_tower,
+ const WipeTowerData &wipe_tower_data,
const FullPrintConfig &config,
- const std::vector &extruders,
+ const std::vector &extruders,
unsigned int initial_extruder_id,
PrintStatistics &print_statistics)
+#endif // ENABLE_BINARIZED_GCODE
{
- std::string filament_stats_string_out;
+ std::string filament_stats_string_out;
- print_statistics.clear();
+ print_statistics.clear();
print_statistics.total_toolchanges = std::max(0, wipe_tower_data.number_of_toolchanges);
print_statistics.initial_extruder_id = initial_extruder_id;
std::vector filament_types;
- if (! extruders.empty()) {
- std::pair out_filament_used_mm ("; filament used [mm] = ", 0);
- std::pair out_filament_used_cm3("; filament used [cm3] = ", 0);
- std::pair out_filament_used_g ("; filament used [g] = ", 0);
- std::pair out_filament_cost ("; filament cost = ", 0);
- for (const Extruder &extruder : extruders) {
+ if (! extruders.empty()) {
+#if ENABLE_BINARIZED_GCODE
+ std::pair out_filament_used_mm(PrintStatistics::FilamentUsedMmMask + " ", 0);
+ std::pair out_filament_used_cm3(PrintStatistics::FilamentUsedCm3Mask + " ", 0);
+ std::pair out_filament_used_g(PrintStatistics::FilamentUsedGMask + " ", 0);
+ std::pair out_filament_cost(PrintStatistics::FilamentCostMask + " ", 0);
+#else
+ std::pair out_filament_used_mm("; filament used [mm] = ", 0);
+ std::pair out_filament_used_cm3("; filament used [cm3] = ", 0);
+ std::pair out_filament_used_g ("; filament used [g] = ", 0);
+ std::pair out_filament_cost("; filament cost = ", 0);
+#endif // ENABLE_BINARIZED_GCODE
+ for (const Extruder &extruder : extruders) {
print_statistics.printing_extruders.emplace_back(extruder.id());
filament_types.emplace_back(config.filament_type.get_at(extruder.id()));
- double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] : 0.f);
- double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter
- double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
- double filament_cost = filament_weight * extruder.filament_cost() * 0.001;
+ double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] : 0.f);
+ double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter
+ double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
+ double filament_cost = filament_weight * extruder.filament_cost() * 0.001;
auto append = [&extruder](std::pair &dst, const char *tmpl, double value) {
assert(is_decimal_separator_point());
- while (dst.second < extruder.id()) {
- // Fill in the non-printing extruders with zeros.
- dst.first += (dst.second > 0) ? ", 0" : "0";
- ++ dst.second;
- }
- if (dst.second > 0)
- dst.first += ", ";
- char buf[64];
- sprintf(buf, tmpl, value);
- dst.first += buf;
- ++ dst.second;
- };
- append(out_filament_used_mm, "%.2lf", used_filament);
- append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001);
- if (filament_weight > 0.) {
- print_statistics.total_weight = print_statistics.total_weight + filament_weight;
- append(out_filament_used_g, "%.2lf", filament_weight);
- if (filament_cost > 0.) {
- print_statistics.total_cost = print_statistics.total_cost + filament_cost;
- append(out_filament_cost, "%.2lf", filament_cost);
- }
- }
- print_statistics.total_used_filament += used_filament;
- print_statistics.total_extruded_volume += extruded_volume;
- print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.;
- print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.;
- }
- filament_stats_string_out += out_filament_used_mm.first;
- filament_stats_string_out += "\n" + out_filament_used_cm3.first;
- if (out_filament_used_g.second)
- filament_stats_string_out += "\n" + out_filament_used_g.first;
- if (out_filament_cost.second)
- filament_stats_string_out += "\n" + out_filament_cost.first;
+ while (dst.second < extruder.id()) {
+ // Fill in the non-printing extruders with zeros.
+ dst.first += (dst.second > 0) ? ", 0" : "0";
+ ++ dst.second;
+ }
+ if (dst.second > 0)
+ dst.first += ", ";
+ char buf[64];
+ sprintf(buf, tmpl, value);
+ dst.first += buf;
+ ++ dst.second;
+ };
+#if ENABLE_BINARIZED_GCODE
+ if (!export_binary_data) {
+#endif // ENABLE_BINARIZED_GCODE
+ append(out_filament_used_mm, "%.2lf", used_filament);
+ append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001);
+#if ENABLE_BINARIZED_GCODE
+ }
+#endif // ENABLE_BINARIZED_GCODE
+ if (filament_weight > 0.) {
+ print_statistics.total_weight = print_statistics.total_weight + filament_weight;
+#if ENABLE_BINARIZED_GCODE
+ if (!export_binary_data)
+#endif // ENABLE_BINARIZED_GCODE
+ append(out_filament_used_g, "%.2lf", filament_weight);
+ if (filament_cost > 0.) {
+ print_statistics.total_cost = print_statistics.total_cost + filament_cost;
+#if ENABLE_BINARIZED_GCODE
+ if (!export_binary_data)
+#endif // ENABLE_BINARIZED_GCODE
+ append(out_filament_cost, "%.2lf", filament_cost);
+ }
+ }
+ print_statistics.total_used_filament += used_filament;
+ print_statistics.total_extruded_volume += extruded_volume;
+ print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.;
+ print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.;
+ }
+#if ENABLE_BINARIZED_GCODE
+ if (!export_binary_data) {
+#endif // ENABLE_BINARIZED_GCODE
+ filament_stats_string_out += out_filament_used_mm.first;
+ filament_stats_string_out += "\n" + out_filament_used_cm3.first;
+ if (out_filament_used_g.second)
+ filament_stats_string_out += "\n" + out_filament_used_g.first;
+ if (out_filament_cost.second)
+ filament_stats_string_out += "\n" + out_filament_cost.first;
+#if ENABLE_BINARIZED_GCODE
+ }
+#endif // ENABLE_BINARIZED_GCODE
print_statistics.initial_filament_type = config.filament_type.get_at(initial_extruder_id);
std::sort(filament_types.begin(), filament_types.end());
print_statistics.printing_filament_types = filament_types.front();
@@ -830,6 +873,94 @@ static inline GCode::SmoothPathCache smooth_path_interpolate_global(const Print&
void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb)
{
+#if ENABLE_BINARIZED_GCODE
+ const bool export_to_binary_gcode = print.full_print_config().option("gcode_binary")->value;
+ // if exporting gcode in binary format:
+ // we generate here the data to be passed to the post-processor, who is responsible to export them to file
+ // 1) generate the thumbnails
+ // 2) collect the config data
+ if (export_to_binary_gcode) {
+ bgcode::binarize::BinaryData& binary_data = m_processor.get_binary_data();
+
+ // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format".
+ // If "thumbnails_format" is not defined, export to PNG.
+ if (std::vector> thumbnails = GCodeThumbnails::make_thumbnail_list(print.full_print_config());
+ ! thumbnails.empty())
+ GCodeThumbnails::generate_binary_thumbnails(
+ thumbnail_cb, binary_data.thumbnails, thumbnails,
+ [&print]() { print.throw_if_canceled(); });
+
+ // file data
+ binary_data.file_metadata.raw_data.emplace_back("Producer", std::string(SLIC3R_APP_NAME) + " " + std::string(SLIC3R_VERSION));
+
+ // config data
+ encode_full_config(print, binary_data.slicer_metadata.raw_data);
+
+ // printer data
+ binary_data.printer_metadata.raw_data.emplace_back("printer_model", print.config().printer_model.value); // duplicated into config data
+ std::string filament_types_str;
+ for (size_t i = 0; i < print.config().filament_type.values.size(); ++i) {
+ filament_types_str += print.config().filament_type.values[i];
+ if (i < print.config().filament_type.values.size() - 1)
+ filament_types_str += ";";
+ }
+ binary_data.printer_metadata.raw_data.emplace_back("filament_type", filament_types_str); // duplicated into config data
+ char buf[1024];
+ std::string nozzle_diameters_str;
+ for (size_t i = 0; i < print.config().nozzle_diameter.values.size(); ++i) {
+ sprintf(buf, i < print.config().nozzle_diameter.values.size() - 1 ? "%.2g," : "%.2g", print.config().nozzle_diameter.values[i]);
+ nozzle_diameters_str += buf;
+ }
+ binary_data.printer_metadata.raw_data.emplace_back("nozzle_diameter", nozzle_diameters_str); // duplicated into config data
+ std::string bed_temperatures_str;
+ for (size_t i = 0; i < print.config().bed_temperature.values.size(); ++i) {
+ sprintf(buf, i < print.config().bed_temperature.values.size() - 1 ? "%d," : "%d", print.config().bed_temperature.values[i]);
+ bed_temperatures_str += buf;
+ }
+ binary_data.printer_metadata.raw_data.emplace_back("bed_temperature", bed_temperatures_str); // duplicated into config data
+
+ const DynamicPrintConfig& cfg = print.full_print_config();
+ if (auto opt = cfg.option("brim_width"); opt != nullptr) {
+ sprintf(buf, "%.2g", dynamic_cast(opt)->value);
+ binary_data.printer_metadata.raw_data.emplace_back("brim_width", buf); // duplicated into config data
+ }
+ if (auto opt = cfg.option("fill_density"); opt != nullptr) {
+ sprintf(buf, "%.2g%%", dynamic_cast(opt)->value);
+ binary_data.printer_metadata.raw_data.emplace_back("fill_density", buf); // duplicated into config data
+ }
+ if (auto opt = cfg.option("layer_height"); opt != nullptr) {
+ sprintf(buf, "%.2g", dynamic_cast(opt)->value);
+ binary_data.printer_metadata.raw_data.emplace_back("layer_height", buf); // duplicated into config data
+ }
+ if (auto opt = cfg.option("temperature"); opt != nullptr) {
+ auto values = dynamic_cast(opt)->values;
+ std::string temperatures_str;
+ for (size_t i = 0; i < values.size(); ++i) {
+ sprintf(buf, i < values.size() - 1 ? "%d," : "%d", values[i]);
+ temperatures_str += buf;
+ }
+ binary_data.printer_metadata.raw_data.emplace_back("temperature", temperatures_str); // duplicated into config data
+ }
+ if (auto opt = cfg.option("ironing"); opt != nullptr)
+ binary_data.printer_metadata.raw_data.emplace_back("ironing", dynamic_cast(opt)->value ? "1" : "0"); // duplicated into config data
+ if (auto opt = cfg.option("support_material"); opt != nullptr)
+ binary_data.printer_metadata.raw_data.emplace_back("support_material", dynamic_cast(opt)->value ? "1" : "0"); // duplicated into config data
+ if (auto opt = cfg.option("extruder_colour"); opt != nullptr) {
+ auto values = dynamic_cast(opt)->values;
+ std::string extruder_colours_str;
+ if (values.size() == 1 && values.front().empty())
+ extruder_colours_str = "\"\"";
+ else {
+ for (size_t i = 0; i < values.size(); ++i) {
+ sprintf(buf, i < values.size() - 1 ? "%s;" : "%s", values[i].c_str());
+ extruder_colours_str += buf;
+ }
+ }
+ binary_data.printer_metadata.raw_data.emplace_back("extruder_colour", extruder_colours_str); // duplicated into config data
+ }
+ }
+#endif // ENABLE_BINARIZED_GCODE
+
// modifies m_silent_time_estimator_enabled
DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled);
@@ -883,42 +1014,24 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
this->m_avoid_crossing_curled_overhangs.init_bed_shape(get_bed_shape(print.config()));
}
- // Write information on the generator.
- file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str());
+#if ENABLE_BINARIZED_GCODE
+ if (!export_to_binary_gcode)
+#endif // ENABLE_BINARIZED_GCODE
+ // Write information on the generator.
+ file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str());
- // ??? Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format".
- // ??? If "thumbnails_format" is not defined, export to PNG.
-
- // generate thumbnails data to process it
-
- std::vector> thumbnails_list;
- if (const auto thumbnails_value = print.full_print_config().option("thumbnails")) {
- std::string str = thumbnails_value->value;
- std::istringstream is(str);
- std::string point_str;
- while (std::getline(is, point_str, ',')) {
- Vec2d point(Vec2d::Zero());
- GCodeThumbnailsFormat format;
- std::istringstream iss(point_str);
- std::string coord_str;
- if (std::getline(iss, coord_str, 'x')) {
- std::istringstream(coord_str) >> point(0);
- if (std::getline(iss, coord_str, '/')) {
- std::istringstream(coord_str) >> point(1);
- std::string ext_str;
- if (std::getline(iss, ext_str, '/'))
- format = ext_str == "JPG" ? GCodeThumbnailsFormat::JPG :
- ext_str == "QOI" ? GCodeThumbnailsFormat::QOI :GCodeThumbnailsFormat::PNG;
- }
- }
- thumbnails_list.emplace_back(std::make_pair(format, point));
- }
+#if ENABLE_BINARIZED_GCODE
+ // if exporting gcode in ascii format, generate the thumbnails here
+ if (! export_to_binary_gcode) {
+#endif // ENABLE_BINARIZED_GCODE
+ if (std::vector> thumbnails = GCodeThumbnails::make_thumbnail_list(print.full_print_config());
+ ! thumbnails.empty())
+ GCodeThumbnails::export_thumbnails_to_file(thumbnail_cb, thumbnails,
+ [&file](const char* sz) { file.write(sz); },
+ [&print]() { print.throw_if_canceled(); });
+#if ENABLE_BINARIZED_GCODE
}
-
- if (!thumbnails_list.empty())
- GCodeThumbnails::export_thumbnails_to_file(thumbnail_cb, thumbnails_list,
- [&file](const char* sz) { file.write(sz); },
- [&print]() { print.throw_if_canceled(); });
+#endif // ENABLE_BINARIZED_GCODE
// Write notes (content of the Print Settings tab -> Notes)
{
@@ -940,20 +1053,26 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
const double layer_height = first_object->config().layer_height.value;
assert(! print.config().first_layer_height.percent);
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 ®ion = print.get_print_region(region_id);
- 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())
- file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
- if (print.config().first_layer_extrusion_width.value > 0)
- file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width());
- file.write_format("\n");
+#if ENABLE_BINARIZED_GCODE
+ if (!export_to_binary_gcode) {
+#endif // ENABLE_BINARIZED_GCODE
+ for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) {
+ const PrintRegion ®ion = print.get_print_region(region_id);
+ 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())
+ file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
+ if (print.config().first_layer_extrusion_width.value > 0)
+ 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();
+#if ENABLE_BINARIZED_GCODE
}
- print.throw_if_canceled();
+#endif // ENABLE_BINARIZED_GCODE
// adds tags for time estimators
if (print.config().remaining_times.value)
@@ -1267,31 +1386,72 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
print.throw_if_canceled();
// Get filament stats.
- file.write(DoExport::update_print_stats_and_format_filament_stats(
- // Const inputs
+#if ENABLE_BINARIZED_GCODE
+ const std::string filament_stats_string_out = DoExport::update_print_stats_and_format_filament_stats(
+ // Const inputs
has_wipe_tower, print.wipe_tower_data(),
this->config(),
m_writer.extruders(),
initial_extruder_id,
// Modifies
- print.m_print_statistics));
- 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)
- 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());
+ print.m_print_statistics,
+ export_to_binary_gcode,
+ m_processor.get_binary_data()
+ );
- // 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.
- {
- file.write("\n; prusaslicer_config = begin\n");
- std::string full_config;
- append_full_config(print, full_config);
- if (!full_config.empty())
- file.write(full_config);
- file.write("; prusaslicer_config = end\n");
+ if (!export_to_binary_gcode)
+ file.write(filament_stats_string_out);
+
+ if (export_to_binary_gcode) {
+ bgcode::binarize::BinaryData& binary_data = m_processor.get_binary_data();
+ if (print.m_print_statistics.total_toolchanges > 0)
+ binary_data.print_metadata.raw_data.emplace_back("total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges));
+ char buf[1024];
+ sprintf(buf, "%.2lf", m_max_layer_z);
+ binary_data.printer_metadata.raw_data.emplace_back("max_layer_z", buf);
}
+ else {
+#else
+ file.write(DoExport::update_print_stats_and_format_filament_stats(
+ // Const inputs
+ has_wipe_tower, print.wipe_tower_data(),
+ this->config(),
+ m_writer.extruders(),
+ initial_extruder_id,
+ // Modifies
+ print.m_print_statistics));
+#endif // ENABLE_BINARIZED_GCODE
+#if ENABLE_BINARIZED_GCODE
+ // if exporting gcode in ascii format, statistics export is done here
+#endif // ENABLE_BINARIZED_GCODE
+ file.write("\n");
+#if ENABLE_BINARIZED_GCODE
+ file.write_format(PrintStatistics::TotalFilamentUsedGValueMask.c_str(), print.m_print_statistics.total_weight);
+ file.write_format(PrintStatistics::TotalFilamentCostValueMask.c_str(), print.m_print_statistics.total_cost);
+#else
+ 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);
+#endif // ENABLE_BINARIZED_GCODE
+ if (print.m_print_statistics.total_toolchanges > 0)
+ 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());
+
+#if ENABLE_BINARIZED_GCODE
+ // if exporting gcode in ascii format, config export is done here
+#endif // ENABLE_BINARIZED_GCODE
+ // 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.
+ {
+ file.write("\n; prusaslicer_config = begin\n");
+ std::string full_config;
+ append_full_config(print, full_config);
+ if (!full_config.empty())
+ file.write(full_config);
+ file.write("; prusaslicer_config = end\n");
+ }
+#if ENABLE_BINARIZED_GCODE
+ }
+#endif // ENABLE_BINARIZED_GCODE
print.throw_if_canceled();
}
@@ -2400,6 +2560,13 @@ void GCodeGenerator::apply_print_config(const PrintConfig &print_config)
void GCodeGenerator::append_full_config(const Print &print, std::string &str)
{
+#if ENABLE_BINARIZED_GCODE
+ std::vector> config;
+ encode_full_config(print, config);
+ for (const auto& [key, value] : config) {
+ str += "; " + key + " = " + value + "\n";
+ }
+#else
const DynamicPrintConfig &cfg = print.full_print_config();
// Sorted list of config keys, which shall not be stored into the G-code. Initializer list.
static constexpr auto banned_keys = {
@@ -2417,8 +2584,35 @@ void GCodeGenerator::append_full_config(const Print &print, std::string &str)
for (const std::string &key : cfg.keys())
if (! is_banned(key) && ! cfg.option(key)->is_nil())
str += "; " + key + " = " + cfg.opt_serialize(key) + "\n";
+#endif // ENABLE_BINARIZED_GCODE
}
+#if ENABLE_BINARIZED_GCODE
+void GCodeGenerator::encode_full_config(const Print& print, std::vector>& config)
+{
+ const DynamicPrintConfig& cfg = print.full_print_config();
+ // Sorted list of config keys, which shall not be stored into the G-code. Initializer list.
+ static constexpr auto banned_keys = {
+ "compatible_printers"sv,
+ "compatible_prints"sv,
+ //FIXME The print host keys should not be exported to full_print_config anymore. The following keys may likely be removed.
+ "print_host"sv,
+ "printhost_apikey"sv,
+ "printhost_cafile"sv
+ };
+ assert(std::is_sorted(banned_keys.begin(), banned_keys.end()));
+ auto is_banned = [](const std::string& key) {
+ return std::binary_search(banned_keys.begin(), banned_keys.end(), key);
+ };
+ config.reserve(config.size() + cfg.keys().size());
+ for (const std::string& key : cfg.keys()) {
+ if (!is_banned(key) && !cfg.option(key)->is_nil())
+ config.emplace_back(key, cfg.opt_serialize(key));
+ }
+ config.shrink_to_fit();
+}
+#endif // ENABLE_BINARIZED_GCODE
+
void GCodeGenerator::set_extruders(const std::vector &extruder_ids)
{
m_writer.set_extruders(extruder_ids);
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index c3b5bc8c11..1d602a9260 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -30,6 +30,12 @@
#include