diff --git a/CMakeLists.txt b/CMakeLists.txt index 8557ab0d7..72fd87d22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1) option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1) option(SLIC3R_PERL_XS "Compile XS Perl module and enable Perl unit and integration tests" 0) option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0) +option(SLIC3R_UBSAN "Enable UBSan on Clang and GCC" 0) # If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, othrewise variable. CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow perfoming desktop integration during runtime" 1 "NOT SLIC3R_FHS" 0) @@ -239,6 +240,11 @@ if (NOT MSVC AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMP add_compile_options(-Wno-deprecated-declarations) endif() + # Clang reports misleading indentation for some IF blocks because of mixing tabs with spaces. + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + add_compile_options(-Wno-misleading-indentation) + endif() + #GCC generates loads of -Wunknown-pragmas when compiling igl. The fix is not easy due to a bug in gcc, see # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66943 or # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431 @@ -266,6 +272,32 @@ if (SLIC3R_ASAN) endif () endif () +if (SLIC3R_UBSAN) + # Stacktrace for every report is enabled by default. It can be disabled by running PrusaSlicer with "UBSAN_OPTIONS=print_stacktrace=0". + + # Define macro SLIC3R_UBSAN to allow detection in the source code if this sanitizer is enabled. + add_compile_definitions(SLIC3R_UBSAN) + + # Clang supports much more useful checks than GCC, so when Clang is detected, another checks will be enabled. + # List of what GCC is checking: https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html + # List of what Clang is checking: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(_ubsan_flags "-fsanitize=undefined,integer") + else () + set(_ubsan_flags "-fsanitize=undefined") + endif () + + add_compile_options(${_ubsan_flags} -fno-omit-frame-pointer) + + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${_ubsan_flags}") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${_ubsan_flags}") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${_ubsan_flags}") + + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lubsan") + endif () +endif () + if (APPLE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=partial-availability -Werror=unguarded-availability -Werror=unguarded-availability-new") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=partial-availability -Werror=unguarded-availability -Werror=unguarded-availability-new") diff --git a/deps/Boost/Boost.cmake b/deps/Boost/Boost.cmake index ec8bab799..15792d5a7 100644 --- a/deps/Boost/Boost.cmake +++ b/deps/Boost/Boost.cmake @@ -120,6 +120,12 @@ set(_build_cmd ${_build_cmd} set(_install_cmd ${_build_cmd} --prefix=${_prefix} install) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + # When Clang is used with enabled UndefinedBehaviorSanitizer, it produces "undefined reference to '__muloti4'" when __int128 is used. + # Because of that, UndefinedBehaviorSanitizer is disabled for those functions that use __int128. + list(APPEND _patch_command COMMAND ${PATCH_CMD} ${CMAKE_CURRENT_LIST_DIR}/Boost.patch) +endif () + ExternalProject_Add( dep_Boost URL "https://boostorg.jfrog.io/artifactory/main/release/1.75.0/source/boost_1_75_0.tar.gz" diff --git a/deps/Boost/Boost.patch b/deps/Boost/Boost.patch new file mode 100644 index 000000000..8c54430b9 --- /dev/null +++ b/deps/Boost/Boost.patch @@ -0,0 +1,23 @@ +diff -u ../boost_1_75_0-orig/boost/rational.hpp ./boost/rational.hpp +--- ../boost_1_75_0-orig/boost/rational.hpp 2020-12-03 06:02:19.000000000 +0100 ++++ ./boost/rational.hpp 2022-01-27 16:02:27.993848905 +0100 +@@ -302,6 +302,9 @@ + return *this; + } + template ++ #if defined(__clang__) ++ __attribute__((no_sanitize("undefined"))) ++ #endif + BOOST_CXX14_CONSTEXPR typename boost::enable_if_c::value, rational&>::type operator*= (const T& i) + { + // Avoid overflow and preserve normalization +@@ -311,6 +314,9 @@ + return *this; + } + template ++ #if defined(__clang__) ++ __attribute__((no_sanitize("undefined"))) ++ #endif + BOOST_CXX14_CONSTEXPR typename boost::enable_if_c::value, rational&>::type operator/= (const T& i) + { + // Avoid repeated construction diff --git a/resources/profiles/Jubilee.ini b/resources/profiles/Jubilee.ini index 477b6a1e3..d9ab64bd7 100644 --- a/resources/profiles/Jubilee.ini +++ b/resources/profiles/Jubilee.ini @@ -498,6 +498,7 @@ use_volumetric_e = 0 variable_layer_height = 0 wipe = 0,0 z_offset = 0 +before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0 default_filament_profile = "Generic PLA @Jubilee; Generic PLA @Jubilee" default_print_profile = 0.2mm V6, aesthetic @Jubilee diff --git a/resources/profiles/RatRig.ini b/resources/profiles/RatRig.ini index ff18030d3..69379e781 100644 --- a/resources/profiles/RatRig.ini +++ b/resources/profiles/RatRig.ini @@ -384,7 +384,7 @@ top_solid_infill_speed = 60% # Common printer preset [printer:*common*] -before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\n;{if layer_num == 2 }SET_FILAMENT_SENSOR SENSOR=my_sensor ENABLE=1{endif}\n\n +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n;{if layer_num == 2 }SET_FILAMENT_SENSOR SENSOR=my_sensor ENABLE=1{endif}\n\n between_objects_gcode = color_change_gcode = M600 cooling_tube_length = 5 @@ -453,7 +453,7 @@ printer_variant = 0.4 [printer:*vminion-klipper*] inherits = *common* -before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\n;{if layer_num == 2 }SET_FILAMENT_SENSOR SENSOR=my_sensor ENABLE=1{endif}\n\n +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n;{if layer_num == 2 }SET_FILAMENT_SENSOR SENSOR=my_sensor ENABLE=1{endif}\n\n default_filament_profile = "Generic PLA @RatRig" default_print_profile = 0.20mm NORMAL V-Minion @RatRig deretract_speed = 40 diff --git a/resources/profiles/Voron.ini b/resources/profiles/Voron.ini index fb024870a..cd0fbd2d9 100644 --- a/resources/profiles/Voron.ini +++ b/resources/profiles/Voron.ini @@ -168,7 +168,7 @@ default_print_profile = 0.4mm 1.2nozzle # Common printer preset [printer:*common*] printer_technology = FFF -before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\n\n +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n between_objects_gcode = deretract_speed = 25 end_gcode = print_end ;end script from macro diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 78e3623d9..2648fba9e 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -843,6 +843,15 @@ extern "C" { } #endif +#if defined(SLIC3R_UBSAN) +extern "C" { + // Enable printing stacktrace by default. It can be disabled by running PrusaSlicer with "UBSAN_OPTIONS=print_stacktrace=0". + const char *__ubsan_default_options() { + return "print_stacktrace=1"; + } +} +#endif + #if defined(_MSC_VER) || defined(__MINGW32__) extern "C" { __declspec(dllexport) int __stdcall slic3r_main(int argc, wchar_t **argv) diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index 69476175e..7d868860a 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -2391,7 +2391,7 @@ static std::vector chain_monotonic_regions( // Probability (unnormalized) of traversing a link between two monotonic regions. auto path_probability = [ -#ifndef __APPLE__ +#if !defined(__APPLE__) && !defined(__clang__) // clang complains when capturing constexpr constants. pheromone_alpha, pheromone_beta #endif // __APPLE__ diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 235cdecb5..a8b66b15b 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -283,25 +283,28 @@ void AMFParserContext::startElement(const char *name, const char **atts) m_value[0] = type; node_type_new = NODE_TYPE_METADATA; } - } else if (strcmp(name, "material") == 0) { + } + else if (strcmp(name, "material") == 0) { const char *material_id = get_attribute(atts, "id"); m_material = m_model.add_material((material_id == nullptr) ? "_" : material_id); node_type_new = NODE_TYPE_MATERIAL; - } else if (strcmp(name, "object") == 0) { + } + else if (strcmp(name, "object") == 0) { const char *object_id = get_attribute(atts, "id"); if (object_id == nullptr) this->stop(); else { assert(m_object_vertices.empty()); m_object = m_model.add_object(); + m_object->name = std::string(object_id); m_object_instances_map[object_id].idx = int(m_model.objects.size())-1; node_type_new = NODE_TYPE_OBJECT; } - } else if (strcmp(name, "constellation") == 0) { - node_type_new = NODE_TYPE_CONSTELLATION; - } else if (strcmp(name, "custom_gcodes_per_height") == 0) { - node_type_new = NODE_TYPE_CUSTOM_GCODE; } + else if (strcmp(name, "constellation") == 0) + node_type_new = NODE_TYPE_CONSTELLATION; + else if (strcmp(name, "custom_gcodes_per_height") == 0) + node_type_new = NODE_TYPE_CUSTOM_GCODE; break; case 2: if (strcmp(name, "metadata") == 0) { @@ -309,12 +312,14 @@ void AMFParserContext::startElement(const char *name, const char **atts) m_value[0] = get_attribute(atts, "type"); node_type_new = NODE_TYPE_METADATA; } - } else if (strcmp(name, "layer_config_ranges") == 0 && m_path[1] == NODE_TYPE_OBJECT) + } + else if (strcmp(name, "layer_config_ranges") == 0 && m_path[1] == NODE_TYPE_OBJECT) node_type_new = NODE_TYPE_LAYER_CONFIG; else if (strcmp(name, "mesh") == 0) { if (m_path[1] == NODE_TYPE_OBJECT) node_type_new = NODE_TYPE_MESH; - } else if (strcmp(name, "instance") == 0) { + } + else if (strcmp(name, "instance") == 0) { if (m_path[1] == NODE_TYPE_CONSTELLATION) { const char *object_id = get_attribute(atts, "objectid"); if (object_id == nullptr) @@ -910,12 +915,17 @@ bool load_amf_file(const char *path, DynamicPrintConfig *config, ConfigSubstitut if (result) ctx.endDocument(); - for (ModelObject* o : model->objects) - { - for (ModelVolume* v : o->volumes) - { - if (v->source.input_file.empty() && (v->type() == ModelVolumeType::MODEL_PART)) + for (ModelObject* o : model->objects) { + unsigned int counter = 0; + for (ModelVolume* v : o->volumes) { + ++counter; + if (v->source.input_file.empty() && v->type() == ModelVolumeType::MODEL_PART) v->source.input_file = path; + if (v->name.empty()) { + v->name = o->name; + if (o->volumes.size() > 1) + v->name += "_" + std::to_string(counter); + } } } diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index f60ad896b..995964eb5 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1188,6 +1188,7 @@ void GCodeProcessor::reset() m_start_position = { 0.0f, 0.0f, 0.0f, 0.0f }; m_end_position = { 0.0f, 0.0f, 0.0f, 0.0f }; + m_saved_position = { 0.0f, 0.0f, 0.0f, 0.0f }; m_origin = { 0.0f, 0.0f, 0.0f, 0.0f }; m_cached_position.reset(); m_wiping = false; @@ -1616,6 +1617,13 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool default: break; } break; + case '6': + switch (cmd[2]) { + case '0': { process_G60(line); break; } // Save Current Position + case '1': { process_G61(line); break; } // Return to Saved Position + default: break; + } + break; case '9': switch (cmd[2]) { case '0': { process_G90(line); break; } // Set to Absolute Positioning @@ -2828,6 +2836,43 @@ void GCodeProcessor::process_G28(const GCodeReader::GCodeLine& line) process_G1(new_gline); } +void GCodeProcessor::process_G60(const GCodeReader::GCodeLine& line) +{ + if (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) + m_saved_position = m_end_position; +} + +void GCodeProcessor::process_G61(const GCodeReader::GCodeLine& line) +{ + if (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) { + bool modified = false; + if (line.has_x()) { + m_end_position[X] = m_saved_position[X]; + modified = true; + } + if (line.has_y()) { + m_end_position[Y] = m_saved_position[Y]; + modified = true; + } + if (line.has_z()) { + m_end_position[Z] = m_saved_position[Z]; + modified = true; + } + if (line.has_e()) { + m_end_position[E] = m_saved_position[E]; + modified = true; + } + if (line.has_f()) + m_feedrate = line.f(); + + if (!modified) + m_end_position = m_saved_position; + + + store_move_vertex(EMoveType::Travel); + } +} + void GCodeProcessor::process_G90(const GCodeReader::GCodeLine& line) { m_global_positioning_type = EPositioningType::Absolute; diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 2f3ee4e5d..153f4a9c5 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -517,6 +517,7 @@ namespace Slic3r { AxisCoords m_start_position; // mm AxisCoords m_end_position; // mm + AxisCoords m_saved_position; // mm AxisCoords m_origin; // mm CachedPosition m_cached_position; bool m_wiping; @@ -661,6 +662,12 @@ namespace Slic3r { // Move to origin void process_G28(const GCodeReader::GCodeLine& line); + // Save Current Position + void process_G60(const GCodeReader::GCodeLine& line); + + // Return to Saved Position + void process_G61(const GCodeReader::GCodeLine& line); + // Set to Absolute Positioning void process_G90(const GCodeReader::GCodeLine& line); diff --git a/src/libslic3r/Int128.hpp b/src/libslic3r/Int128.hpp index 8dc9e012d..7aeacbfce 100644 --- a/src/libslic3r/Int128.hpp +++ b/src/libslic3r/Int128.hpp @@ -105,6 +105,11 @@ public: static inline Int128 multiply(int64_t lhs, int64_t rhs) { return Int128(__int128(lhs) * __int128(rhs)); } +#if defined(__clang__) + // When Clang is used with enabled UndefinedBehaviorSanitizer, it produces "undefined reference to '__muloti4'" when __int128 is used. + // Because of that, UndefinedBehaviorSanitizer is disabled for this function. + __attribute__((no_sanitize("undefined"))) +#endif // Evaluate signum of a 2x2 determinant. static int sign_determinant_2x2(int64_t a11, int64_t a12, int64_t a21, int64_t a22) { diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index efc66f478..67450fb11 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2264,7 +2264,7 @@ void check_model_ids_validity(const Model &model) for (const ModelInstance *model_instance : model_object->instances) check(model_instance->id()); } - for (const auto mm : model.materials) { + for (const auto &mm : model.materials) { check(mm.second->id()); check(mm.second->config.id()); } diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 935348279..b5208b1d8 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -17,7 +17,8 @@ class MultiPoint public: Points points; - MultiPoint() {} + MultiPoint() = default; + virtual ~MultiPoint() = default; MultiPoint(const MultiPoint &other) : points(other.points) {} MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {} MultiPoint(std::initializer_list list) : points(list) {} diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 7d34e3aae..d24540339 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -19,7 +19,7 @@ class Polygon : public MultiPoint { public: Polygon() = default; - virtual ~Polygon() = default; + ~Polygon() override = default; explicit Polygon(const Points &points) : MultiPoint(points) {} Polygon(std::initializer_list points) : MultiPoint(points) {} Polygon(const Polygon &other) : MultiPoint(other.points) {} diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 31e0b88d3..5766d9671 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -16,7 +16,8 @@ typedef std::vector ThickPolylines; class Polyline : public MultiPoint { public: - Polyline() {}; + Polyline() = default; + ~Polyline() override = default; Polyline(const Polyline &other) : MultiPoint(other.points) {} Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {} Polyline(std::initializer_list list) : MultiPoint(list) {} diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index e8078931b..dc47b382d 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -22,6 +22,7 @@ #include #include #include +#include // Mark string for localization and translate. #define L(s) Slic3r::I18N::translate(s) @@ -438,7 +439,8 @@ static inline bool sequential_print_vertical_clearance_valid(const Print &print) return it == print_instances_ordered.end() || (*it)->print_object->height() <= scale_(print.config().extruder_clearance_height.value); } - +// Matches "G92 E0" with various forms of writing the zero and with an optional comment. +boost::regex regex_g92e0 { "^[ \\t]*[gG]92[ \\t]*[eE](0(\\.0*)?|\\.0+)[ \\t]*(;.*)?$" }; // Precondition: Print::validate() requires the Print::apply() to be called its invocation. std::string Print::validate(std::string* warning) const @@ -652,6 +654,18 @@ std::string Print::validate(std::string* warning) const return err_msg; } } + { + bool before_layer_gcode_resets_extruder = boost::regex_search(m_config.before_layer_gcode.value, regex_g92e0); + bool layer_gcode_resets_extruder = boost::regex_search(m_config.layer_gcode.value, regex_g92e0); + if (m_config.use_relative_e_distances) { + // See GH issues #6336 #5073 + if (! before_layer_gcode_resets_extruder && ! layer_gcode_resets_extruder) + return L("Relative extruder addressing requires resetting the extruder position at each layer to prevent loss of floating point accuracy. Add \"G92 E0\" to layer_gcode."); + } else if (before_layer_gcode_resets_extruder) + return L("\"G92 E0\" was found in before_layer_gcode, which is incompatible with absolute extruder addressing."); + else if (layer_gcode_resets_extruder) + return L("\"G92 E0\" was found in layer_gcode, which is incompatible with absolute extruder addressing."); + } return std::string(); } diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 1a52de389..9a2f690a1 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -26,6 +26,8 @@ #define ENABLE_GCODE_VIEWER_DATA_CHECKING 0 // Enable project dirty state manager debug window #define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW 0 +// Disable using instanced models to render options in gcode preview +#define DISABLE_GCODEVIEWER_INSTANCED_MODELS 1 // Enable rendering of objects using environment map diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 2562d1913..10790ef49 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -78,6 +78,7 @@ extern std::string normalize_utf8_nfc(const char *src); // Returns next utf8 sequence length. =number of bytes in string, that creates together one utf-8 character. // Starting at pos. ASCII characters returns 1. Works also if pos is in the middle of the sequence. extern size_t get_utf8_sequence_length(const std::string& text, size_t pos = 0); +extern size_t get_utf8_sequence_length(const char *seq, size_t size); // Safely rename a file even if the target exists. // On Windows, the file explorer (or anti-virus or whatever else) often locks the file diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index c74e57e99..e00b6e71c 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -866,8 +866,13 @@ std::string normalize_utf8_nfc(const char *src) size_t get_utf8_sequence_length(const std::string& text, size_t pos) { assert(pos < text.size()); + return get_utf8_sequence_length(text.c_str() + pos, text.size() - pos); +} + +size_t get_utf8_sequence_length(const char *seq, size_t size) +{ size_t length = 0; - unsigned char c = text[pos]; + unsigned char c = seq[0]; if (c < 0x80) { // 0x00-0x7F // is ASCII letter length++; @@ -876,8 +881,8 @@ size_t get_utf8_sequence_length(const std::string& text, size_t pos) // pos is in the middle of a utf-8 sequence. Add the utf-8 trailer bytes. else if (c < 0xC0) { // 0x80-0xBF length++; - while (pos + length < text.size()) { - c = text[pos + length]; + while (length < size) { + c = seq[length]; if (c < 0x80 || c >= 0xC0) { break; // prevent overrun } @@ -888,36 +893,36 @@ size_t get_utf8_sequence_length(const std::string& text, size_t pos) // The number of one bits above the topmost zero bit indicates the number of bytes (including this one) in the whole sequence. else if (c < 0xE0) { // 0xC0-0xDF // add a utf-8 sequence (2 bytes) - if (pos + 2 > text.size()) { - return text.size() - pos; // prevent overrun + if (2 > size) { + return size; // prevent overrun } length += 2; } else if (c < 0xF0) { // 0xE0-0xEF // add a utf-8 sequence (3 bytes) - if (pos + 3 > text.size()) { - return text.size() - pos; // prevent overrun + if (3 > size) { + return size; // prevent overrun } length += 3; } else if (c < 0xF8) { // 0xF0-0xF7 // add a utf-8 sequence (4 bytes) - if (pos + 4 > text.size()) { - return text.size() - pos; // prevent overrun + if (4 > size) { + return size; // prevent overrun } length += 4; } else if (c < 0xFC) { // 0xF8-0xFB // add a utf-8 sequence (5 bytes) - if (pos + 5 > text.size()) { - return text.size() - pos; // prevent overrun + if (5 > size) { + return size; // prevent overrun } length += 5; } else if (c < 0xFE) { // 0xFC-0xFD // add a utf-8 sequence (6 bytes) - if (pos + 6 > text.size()) { - return text.size() - pos; // prevent overrun + if (6 > size) { + return size; // prevent overrun } length += 6; } diff --git a/src/slic3r/GUI/FirmwareDialog.cpp b/src/slic3r/GUI/FirmwareDialog.cpp index 7600ef4fa..47402644f 100644 --- a/src/slic3r/GUI/FirmwareDialog.cpp +++ b/src/slic3r/GUI/FirmwareDialog.cpp @@ -654,7 +654,7 @@ void FirmwareDialog::priv::perform_upload() } }) .on_message([ -#ifndef __APPLE__ +#if !defined(__APPLE__) && !defined(__clang__) // clang complains when capturing constants. extra_verbose, #endif // __APPLE__ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 5eea58a0f..367846f0c 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -584,14 +584,16 @@ void GCodeViewer::init() case EMoveType::Retract: case EMoveType::Unretract: case EMoveType::Seam: { -// if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) { -// buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::InstancedModel; -// buffer.shader = "gouraud_light_instanced"; -// buffer.model.model.init_from(diamond(16)); -// buffer.model.color = option_color(type); -// buffer.model.instances.format = InstanceVBuffer::EFormat::InstancedModel; -// } -// else { +#if !DISABLE_GCODEVIEWER_INSTANCED_MODELS + if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) { + buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::InstancedModel; + buffer.shader = "gouraud_light_instanced"; + buffer.model.model.init_from(diamond(16)); + buffer.model.color = option_color(type); + buffer.model.instances.format = InstanceVBuffer::EFormat::InstancedModel; + } + else { +#endif // !DISABLE_GCODEVIEWER_INSTANCED_MODELS buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::BatchedModel; buffer.vertices.format = VBuffer::EFormat::PositionNormal3; buffer.shader = "gouraud_light"; @@ -599,8 +601,10 @@ void GCodeViewer::init() buffer.model.data = diamond(16); buffer.model.color = option_color(type); buffer.model.instances.format = InstanceVBuffer::EFormat::BatchedModel; -// } - break; +#if !DISABLE_GCODEVIEWER_INSTANCED_MODELS + } +#endif // !DISABLE_GCODEVIEWER_INSTANCED_MODELS + break; } case EMoveType::Wipe: case EMoveType::Extrude: { diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b55ba0d75..a33742d14 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -871,8 +871,8 @@ void GUI_App::init_app_config() { // Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release. // SetAppName(SLIC3R_APP_KEY); - SetAppName(SLIC3R_APP_KEY "-alpha"); -// SetAppName(SLIC3R_APP_KEY "-beta"); +// SetAppName(SLIC3R_APP_KEY "-alpha"); + SetAppName(SLIC3R_APP_KEY "-beta"); // SetAppDisplayName(SLIC3R_APP_NAME); // Set the Slic3r data directory at the Slic3r XS module. diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 73d71eead..8dd91759a 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -239,7 +239,8 @@ ObjectList::ObjectList(wxWindow* parent) : ObjectList::~ObjectList() { - delete m_objects_model; + if (m_objects_model) + m_objects_model->DecRef(); } void ObjectList::set_min_height() diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index a6c2dac81..acdbd249c 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -292,42 +292,58 @@ InfoDialog::InfoDialog(wxWindow* parent, const wxString &title, const wxString& finalize(); } -wxString get_wraped_wxString(const wxString& text_in, size_t line_len /*=80*/) +wxString get_wraped_wxString(const wxString& in, size_t line_len /*=80*/) { -#ifdef __WXMSW__ - char slash = '\\'; -#else - char slash = '/'; -#endif - char space = ' '; - char new_line = '\n'; + wxString out; - wxString text = text_in; - - int idx = -1; - size_t cur_len = 0; - size_t text_len = text.Len(); - - for (size_t i = 0; i < text_len; i++) { - cur_len++; - if (text[i] == space || text[i] == slash) - idx = i; - if (text[i] == new_line) { - idx = -1; - cur_len = 0; - continue; + for (size_t i = 0; i < in.size();) { + // Overwrite the character (space or newline) starting at ibreak? + bool overwrite = false; +#if wxUSE_UNICODE_WCHAR + // On Windows, most likely the internal representation of wxString is wide char. + size_t end = std::min(in.size(), i + line_len); + size_t ibreak = end; + for (size_t j = i; j < end; ++ j) { + if (bool newline = in[j] == '\n'; in[j] == ' ' || in[j] == '\t' || newline) { + ibreak = j; + overwrite = true; + if (newline) + break; + } else if (in[j] == '/' || in[j] == '\\') + ibreak = j + 1; } - if (cur_len >= line_len && idx >= 0) { - if (text[idx] == slash) { - text.insert(static_cast(idx) + 1, 1, new_line); - text_len++; +#else + // UTF8 representation of wxString. + // Where to break the line, index of character at the start of a UTF-8 sequence. + size_t ibreak = size_t(-1); + // Overwrite the character at ibreak (it is a whitespace) or not? + for (size_t cnt = 0, j = i; j < in.size();) { + if (bool newline = in[j] == '\n'; in[j] == ' ' || in[j] == '\t' || newline) { + // Overwrite the whitespace. + ibreak = j ++; + overwrite = true; + if (newline) + break; + } else if (in[j] == '/') { + // Insert after the slash. + ibreak = ++ j; + } else + j += get_utf8_sequence_length(in.c_str() + j, in.size() - j); + if (++ cnt == line_len) { + if (ibreak == size_t(-1)) + ibreak = j; + break; } - else // space - text[idx] = new_line; - cur_len = i - static_cast(idx); } +#endif + out.append(in.begin() + i, in.begin() + ibreak); + out.append('\n'); + i = ibreak; + if (overwrite) + ++ i; } - return text; + + return out; } } diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index 61a59f0e3..f3efd0680 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -185,7 +185,7 @@ public: OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false, column_t extra_clmn = nullptr); - ~OptionsGroup() { clear(true); } + virtual ~OptionsGroup() { clear(true); } wxGridSizer* get_grid_sizer() { return m_grid_sizer; } const std::vector& get_lines() { return m_lines; } @@ -253,6 +253,7 @@ public: OptionsGroup(parent, title, is_tab_opt, extra_clmn), m_config(&config->get()), m_modelconfig(config) {} ConfigOptionsGroup( wxWindow* parent) : OptionsGroup(parent, wxEmptyString, true, nullptr) {} + ~ConfigOptionsGroup() override = default; const wxString& config_category() const throw() { return m_config_category; } int config_type() const throw() { return m_config_type; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 0f234f243..0c6de2792 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2594,8 +2594,13 @@ std::vector Plater::priv::load_files(const std::vector& input_ } for (ModelObject* model_object : model.objects) { - if (!type_3mf && !type_zip_amf) + if (!type_3mf && !type_zip_amf) { model_object->center_around_origin(false); + if (type_any_amf && model_object->instances.empty()) { + ModelInstance* instance = model_object->add_instance(); + instance->set_offset(-model_object->origin_translation); + } + } model_object->ensure_on_bed(is_project_file); } @@ -2611,7 +2616,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ } if (one_by_one) { - if (type_3mf && !is_project_file) + if ((type_3mf && !is_project_file) || (type_any_amf && !type_zip_amf)) model.center_instances_around_point(this->bed.build_volume().bed_center()); auto loaded_idxs = load_model_objects(model.objects, is_project_file); obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end()); @@ -3694,12 +3699,20 @@ void Plater::priv::reload_from_disk() if (has_source || has_name) { int new_volume_idx = -1; int new_object_idx = -1; -// if (has_source) { -// // take idxs from source -// new_volume_idx = old_volume->source.volume_idx; -// new_object_idx = old_volume->source.object_idx; -// } -// else { + bool match_found = false; + // take idxs from the matching volume + if (has_source && old_volume->source.object_idx < int(new_model.objects.size())) { + const ModelObject* obj = new_model.objects[old_volume->source.object_idx]; + if (old_volume->source.volume_idx < int(obj->volumes.size())) { + if (obj->volumes[old_volume->source.volume_idx]->name == old_volume->name) { + new_volume_idx = old_volume->source.volume_idx; + new_object_idx = old_volume->source.object_idx; + match_found = true; + } + } + } + + if (!match_found && has_name) { // take idxs from the 1st matching volume for (size_t o = 0; o < new_model.objects.size(); ++o) { ModelObject* obj = new_model.objects[o]; @@ -3715,7 +3728,7 @@ void Plater::priv::reload_from_disk() if (found) break; } -// } + } if (new_object_idx < 0 || int(new_model.objects.size()) <= new_object_idx) { fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name)); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index d1b7c6769..4f19ad806 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1647,18 +1647,6 @@ void TabPrint::build() option.opt.full_width = true; optgroup->append_single_option_line(option); - optgroup = page->new_optgroup(L("Post-processing scripts"), 0); - line = { "", "" }; - line.full_width = 1; - line.widget = [this](wxWindow* parent) { - return description_line_widget(parent, &m_post_process_explanation); - }; - optgroup->append_line(line); - option = optgroup->get_option("post_process"); - option.opt.full_width = true; - option.opt.height = 5;//50; - optgroup->append_single_option_line(option); - optgroup = page->new_optgroup(L("Other")); create_line_with_widget(optgroup.get(), "gcode_substitutions", "g-code-substitutions_301694", [this](wxWindow* parent) { @@ -1671,6 +1659,18 @@ void TabPrint::build() }; optgroup->append_line(line); + optgroup = page->new_optgroup(L("Post-processing scripts"), 0); + line = { "", "" }; + line.full_width = 1; + line.widget = [this](wxWindow* parent) { + return description_line_widget(parent, &m_post_process_explanation); + }; + optgroup->append_line(line); + option = optgroup->get_option("post_process"); + option.opt.full_width = true; + option.opt.height = 5;//50; + optgroup->append_single_option_line(option); + page = add_options_page(L("Notes"), "note.png"); optgroup = page->new_optgroup(L("Notes"), 0); option = optgroup->get_option("notes"); @@ -3967,7 +3967,7 @@ void SubstitutionManager::add_substitution( int substitution_id, editor->SetFont(wxGetApp().normal_font()); wxGetApp().UpdateDarkUI(editor); - top_sizer->Add(editor, proportion, wxALIGN_CENTER_VERTICAL | wxEXPAND| wxRIGHT, m_em); + top_sizer->Add(editor, proportion, wxALIGN_CENTER_VERTICAL | wxRIGHT, m_em); editor->Bind(wxEVT_TEXT_ENTER, [this, editor, substitution_id, opt_pos](wxEvent& e) { #if !defined(__WXGTK__) @@ -4032,7 +4032,7 @@ void SubstitutionManager::add_substitution( int substitution_id, auto v_sizer = new wxBoxSizer(wxVERTICAL); v_sizer->Add(top_sizer, 1, wxEXPAND); v_sizer->Add(params_sizer, 1, wxEXPAND|wxTOP|wxBOTTOM, int(0.5* m_em)); - m_grid_sizer->Add(v_sizer, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND); + m_grid_sizer->Add(v_sizer, 1, wxEXPAND); if (call_after_layout) { m_parent->GetParent()->Layout(); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 808e968b4..d49b4b1d3 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -1158,10 +1158,11 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& } if (opt_key == "gcode_substitutions") { if (!strings->empty()) - for (size_t id = 0; id < strings->size(); id += 3) + for (size_t id = 0; id < strings->size(); id += 4) out += from_u8(strings->get_at(id)) + ";\t" + from_u8(strings->get_at(id + 1)) + ";\t" + - from_u8(strings->get_at(id + 2)) + ";\n"; + from_u8(strings->get_at(id + 2)) + ";\t" + + from_u8(strings->get_at(id + 3)) + ";\n"; return out; } if (!strings->empty() && opt_idx < strings->values.size()) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 115300a18..9061c0ba2 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -204,7 +204,12 @@ class DiffViewCtrl : public wxDataViewCtrl public: DiffViewCtrl(wxWindow* parent, wxSize size); - ~DiffViewCtrl() override { Clear(); delete model; } + ~DiffViewCtrl() override { + if (model) { + Clear(); + model->DecRef(); + } + } DiffModel* model{ nullptr }; diff --git a/t/gcode.t b/t/gcode.t index a43b5049c..b95505e43 100644 --- a/t/gcode.t +++ b/t/gcode.t @@ -133,6 +133,7 @@ use Slic3r::Test; my $config = Slic3r::Config::new_from_defaults; $config->set('retract_length', [1000000]); $config->set('use_relative_e_distances', 1); + $config->set('layer_gcode', "G92 E0\n"); my $print = Slic3r::Test::init_print('20mm_cube', config => $config); Slic3r::Test::gcode($print); ok $print->print->total_used_filament > 0, 'final retraction is not considered in total used filament'; diff --git a/version.inc b/version.inc index c33dc002e..b976d7d67 100644 --- a/version.inc +++ b/version.inc @@ -3,7 +3,7 @@ set(SLIC3R_APP_NAME "PrusaSlicer") set(SLIC3R_APP_KEY "PrusaSlicer") -set(SLIC3R_VERSION "2.4.1-alpha0") +set(SLIC3R_VERSION "2.4.1-beta1") set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN") set(SLIC3R_RC_VERSION "2,4,1,0") set(SLIC3R_RC_VERSION_DOTS "2.4.1.0")