From 836e18b0727ab0e479a3aef9cd97c31a9c9ab06f Mon Sep 17 00:00:00 2001 From: Alexandre Folle de Menezes Date: Mon, 6 Apr 2026 16:27:18 -0300 Subject: [PATCH 1/9] Update pt-BR translations (#12891) --- localization/i18n/pt_BR/OrcaSlicer_pt_BR.po | 82 +++++++-------------- 1 file changed, 28 insertions(+), 54 deletions(-) diff --git a/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po b/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po index b84b0bf417..9e136b4e09 100644 --- a/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po +++ b/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po @@ -4,7 +4,7 @@ msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-20 23:26+0800\n" -"PO-Revision-Date: 2026-03-08 21:15-0300\n" +"PO-Revision-Date: 2026-03-22 17:15-0300\n" "Last-Translator: Alexandre Folle de Menezes\n" "Language-Team: Portuguese, Brazilian\n" "Language: pt_BR\n" @@ -5025,7 +5025,7 @@ msgid "Actual Speed: " msgstr "Velocidade Real: " msgid "PA: " -msgstr "PA: " +msgstr "AP: " msgid "Statistics of All Plates" msgstr "Estatísticas de Todas as Placas" @@ -5436,7 +5436,7 @@ msgid "Return" msgstr "Retornar" msgid "Canvas Toolbar" -msgstr "Barra de ferramentas da tela" +msgstr "Barra de Ferramentas de Tela" msgid "Fit camera to scene or selected object." msgstr "Ajustar câmera à cena ou objeto selecionado." @@ -6061,10 +6061,10 @@ msgid "Retraction test" msgstr "Teste de retração" msgid "Cornering" -msgstr "Curvas" +msgstr "Quinas" msgid "Cornering calibration" -msgstr "Calibração de curvas" +msgstr "Calibração de quinas" msgid "Input Shaping Frequency" msgstr "Frequência da Modelagem de Entrada" @@ -7226,13 +7226,13 @@ msgid "Enable AI monitoring of printing" msgstr "Ativar monitoramento de IA da impressão" msgid "Pausing Sensitivity:" -msgstr "Sensibilidade de pausa:" +msgstr "Sensibilidade de Pausa:" msgid "Spaghetti Detection" msgstr "Detecção de Emaranhado" msgid "Detect spaghetti failures (scattered lose filament)." -msgstr "Detectar falhas de espaguete (filamento solto espalhado)." +msgstr "Detectar falhas de emaranhado (filamento solto espalhado)." msgid "Purge Chute Pile-Up Detection" msgstr "Detecção de Acúmulo na Calha de Purga" @@ -9826,8 +9826,8 @@ msgid "" "Enabling both precise Z height and the prime tower may cause slicing errors. " "Do you still want to enable?" msgstr "" -"Habilitar tanto a altura Z precisa quanto a torre de preparação pode causar " -"erros de fatiamento. Deseja continuar habilitando?" +"Habilitar a altura Z precisa e a torre de preparação juntas pode causar " +"erros de fatiamento. Deseja habilitar mesmo assim?" msgid "" "A prime tower is required for clumping detection. There may be flaws on the " @@ -9841,8 +9841,8 @@ msgid "" "Enabling both precise Z height and the prime tower may cause slicing errors. " "Do you still want to enable precise Z height?" msgstr "" -"Habilitar tanto a altura Z precisa quanto a torre de preparação pode causar " -"erros de fatiamento. Deseja continuar habilitando a altura Z precisa?" +"Habilitar a altura Z precisa e a torre de preparação juntas pode causar " +"erros de fatiamento. Deseja habilitar a altura Z precisa mesmo assim?" msgid "" "A prime tower is required for smooth timelapse. There may be flaws on the " @@ -11154,7 +11154,7 @@ msgid "Configuration package changed" msgstr "Pacote de configuração alterado" msgid "Toolbar" -msgstr "Barra de ferramentas" +msgstr "Barra de Ferramentas" msgid "Objects list" msgstr "Lista de objetos" @@ -11885,7 +11885,7 @@ msgstr "" msgid "" "Enabling both precise Z height and the prime tower may cause slicing errors." msgstr "" -"Habilitar tanto a altura Z precisa quanto a torre de preparação pode causar " +"Habilitar a altura Z precisa e a torre de preparação juntas pode causar " "erros de fatiamento." msgid "" @@ -13947,7 +13947,7 @@ msgstr "" "adaptativa deve ser expandida nas direções XY." msgid "Grab length" -msgstr "Comprimento de agarre" +msgstr "Comprimento de pega" msgid "Extruder Color" msgstr "Cor da Extrusora" @@ -14631,7 +14631,7 @@ msgstr "" "suporte." msgid "Filament ramming length" -msgstr "Comprimento de compactação do filamento" +msgstr "Comprimento de moldeamento do filamento" msgid "" "When changing the extruder, it is recommended to extrude a certain length of " @@ -17427,7 +17427,7 @@ msgid "Purge remaining filament into prime tower." msgstr "Purgar o filamento restante na torre de preparo." msgid "Enable filament ramming" -msgstr "Habilitar compactação de filamento" +msgstr "Habilitar moldeamento de filamento" msgid "No sparse layers (beta)" msgstr "Sem camadas esparsas (beta)" @@ -19904,13 +19904,13 @@ msgid "Record Factor" msgstr "Registrar Fator" msgid "We found the best flow ratio for you" -msgstr "Encontramos o melhor fluxo para você" +msgstr "Encontramos a melhor taxa de fluxo para você" msgid "Flow Ratio" -msgstr "Fluxo" +msgstr "Taxa de Fluxo" msgid "Please input a valid value (0.0 < flow ratio < 2.0)" -msgstr "Por favor insira um valor válido (0.0 < fluxo < 2.0)" +msgstr "Por favor insira um valor válido (0.0 < taxa de fluxo < 2.0)" msgid "Please enter the name of the preset you want to save." msgstr "Por favor insira o nome da predefinição que você deseja salvar." @@ -19932,7 +19932,7 @@ msgstr "Pular Calibração2" #, c-format, boost-format msgid "flow ratio : %s " -msgstr "fluxo: %s " +msgstr "taxa de fluxo: %s " msgid "Please choose a block with smoothest top surface." msgstr "Por favor escolha um bloco com a superfície superior mais lisa." @@ -19948,7 +19948,7 @@ msgid "Complete Calibration" msgstr "Calibração Completa" msgid "Fine Calibration based on flow ratio" -msgstr "Calibração Fina baseada no fluxo" +msgstr "Calibração Fina baseada na taxa de fluxo" msgid "Title" msgstr "Título" @@ -20404,7 +20404,7 @@ msgstr "" "DampingEnd <= 1)" msgid "Cornering test" -msgstr "Teste de curvas" +msgstr "Teste de quinas" msgid "SCV-V2" msgstr "SCV-V2" @@ -20416,11 +20416,11 @@ msgid "End: " msgstr "Final: " msgid "Cornering settings" -msgstr "Configurações de curvas" +msgstr "Configurações de quinas" msgid "Note: Lower values = sharper corners but slower speeds." msgstr "" -"Nota: Valores mais baixos = cantos mais nítidos, mas velocidades mais lentas." +"Nota: Valores mais baixos = quinas mais nítidas, mas velocidades mais lentas." msgid "" "Marlin 2 Junction Deviation detected:\n" @@ -20453,7 +20453,7 @@ msgid "" "(0 <= Cornering <= %s)" msgstr "" "Por favor, insira valores válidos:\n" -"(0 <= Curvas <= %s)" +"(0 <= Quinas <= %s)" #, c-format, boost-format msgid "NOTE: High values may cause Layer shift (>%s)" @@ -22317,7 +22317,7 @@ msgid "Restart Later" msgstr "Reiniciar Depois" msgid "NO RAMMING AT ALL" -msgstr "SEM COMPACTAÇÃO" +msgstr "SEM NENHUM MOLDEAMENTO" msgid "Volumetric speed" msgstr "Velocidade volumétrica" @@ -22770,9 +22770,6 @@ msgstr "" "aumentar adequadamente a temperatura da mesa aquecida pode reduzir a " "probabilidade de empenamento?" -#~ msgid "BambuStudio warning" -#~ msgstr "Aviso do Bambu Studio" - #~ msgid "" #~ "Zero first layer height is invalid.\n" #~ "\n" @@ -22789,30 +22786,12 @@ msgstr "" #~ "A posição da torre de preparo ultrapassou os limites da placa de " #~ "impressão e foi reposicionada para a borda válida mais próxima." -#~ msgid "Check for Update" -#~ msgstr "Verificar Atualizações" - -#~ msgid "Detect spaghetti failure(scattered lose filament)." -#~ msgstr "Detectar falha de emaranhado (filamentos soltos e dispersos)." - -#~ msgid "Rotate of view" -#~ msgstr "Rotação da vista" - -#~ msgid "Move of view" -#~ msgstr "Movimento da vista" - -#~ msgid "Zoom of view" -#~ msgstr "Zoom da vista" - #~ msgid "" #~ "Enabling both precise Z height and the prime tower may cause the size of " #~ "prime tower to increase. Do you still want to enable?" #~ msgstr "" -#~ "Habilitar a altura Z precisa e a torre de preparo pode fazer com que o " -#~ "tamanho da torre de preparo aumente. Deseja habilitar mesmo assim?" - -#~ msgid "Extruders count" -#~ msgstr "Número de extrusoras" +#~ "Habilitar a altura Z precisa e a torre de preparação juntas pode causar " +#~ "o aumento de tamanho da torre de preparo. Deseja habilitar mesmo assim?" #~ msgid "" #~ "Missing BambuSource component registered for media playing! Please re-" @@ -23100,11 +23079,6 @@ msgstr "" #~ msgid "Junction Deviation settings" #~ msgstr "Configurações de Desvio de Junção" -#~ msgid "Note: Lower values = sharper corners but slower speeds" -#~ msgstr "" -#~ "Nota: Valores mais baixos = cantos mais nítidos, mas velocidades mais " -#~ "lentas" - #~ msgid "" #~ "Please input valid values:\n" #~ "(0 <= Junction Deviation < 1)" From 9282d2de1df175e5aa9e648b6b95903226b86d9d Mon Sep 17 00:00:00 2001 From: Vovodroid Date: Tue, 7 Apr 2026 17:20:33 +0300 Subject: [PATCH 2/9] Fix arc fitting time progress with firmware retraction. (#12888) --- src/libslic3r/GCode/GCodeProcessor.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index f7c9d51cb5..8876cbeede 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -4772,17 +4772,21 @@ void GCodeProcessor::process_G29(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_G10(const GCodeReader::GCodeLine& line) { + // Emulate G1 retract, decrement G1 count as it's will be incremented in process_G1, but it's fake G1 GCodeReader::GCodeLine g10; g10.set(Axis::E, -this->m_parser.config().retraction_length.get_at(m_extruder_id)); g10.set(Axis::F, this->m_parser.config().retraction_speed.get_at(m_extruder_id) * 60); + --m_g1_line_id; process_G1(g10); } void GCodeProcessor::process_G11(const GCodeReader::GCodeLine& line) { + // Emulate G1 unretract, decrement G1 count as it's will be incremented in process_G1, but it's fake G1 GCodeReader::GCodeLine g11; g11.set(Axis::E, this->m_parser.config().retraction_length.get_at(m_extruder_id) + this->m_parser.config().retract_restart_extra.get_at(m_extruder_id)); g11.set(Axis::F, this->m_parser.config().deretraction_speed.get_at(m_extruder_id) * 60); + --m_g1_line_id; process_G1(g11); } From 2829b8eabbb370edf91054438f5146ff78571c88 Mon Sep 17 00:00:00 2001 From: mosfet80 <10235105+mosfet80@users.noreply.github.com> Date: Tue, 7 Apr 2026 16:21:26 +0200 Subject: [PATCH 3/9] Update CMake policy version and minimum requirements (#12905) Removed the old control code for cmake inside the CmakeList.txt file --- CMakeLists.txt | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 88cb23129e..3b673708a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,9 @@ if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "4.0") - set(CMAKE_POLICY_VERSION_MINIMUM 3.5 CACHE STRING "" FORCE) + set(CMAKE_POLICY_VERSION_MINIMUM 3.13 CACHE STRING "" FORCE) endif() cmake_minimum_required(VERSION 3.13) -# Verify that your CMake version is exactly 3.5 series or higher on windows -if ( (MSVC OR WIN32) AND (${CMAKE_VERSION} VERSION_LESS "3.5") ) - message(FATAL_ERROR "CMake current version ${CMAKE_VERSION} is too old. Minimum required is 3.5.") -endif() # The following line used to be in tests/CMakeLists.txt # Having it there causes rebuilds of all targets on any CMakeLists.txt change under tests/ @@ -377,11 +373,6 @@ endif () if (CMAKE_SYSTEM_NAME STREQUAL "Linux") find_package(PkgConfig REQUIRED) - if (CMAKE_VERSION VERSION_LESS "3.1") - # Workaround for an old CMake, which does not understand CMAKE_CXX_STANDARD. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - endif() - # Boost on Raspberry-Pi does not link to pthreads. set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) From 9478af93e454d398a9263efad4b82b08862c34cb Mon Sep 17 00:00:00 2001 From: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> Date: Tue, 7 Apr 2026 11:22:43 -0300 Subject: [PATCH 4/9] =?UTF-8?q?=F0=9F=92=A5Fix=20Crash=20with=20painted=20?= =?UTF-8?q?Line=20in=20=20some=20corrupted=20models=20(#12806)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Safeguard EdgeGrid.hpp --- src/libslic3r/EdgeGrid.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libslic3r/EdgeGrid.hpp b/src/libslic3r/EdgeGrid.hpp index 744a23e186..6bbed1e9fa 100644 --- a/src/libslic3r/EdgeGrid.hpp +++ b/src/libslic3r/EdgeGrid.hpp @@ -224,6 +224,8 @@ public: iy += 1; assert(iy <= iyb); } + if (ix < 0 || iy < 0 || ix >= (int64_t)m_cols || iy >= (int64_t)m_rows) + return; if (! visitor(iy, ix)) return; } while (ix != ixb || iy != iyb); @@ -245,6 +247,8 @@ public: iy -= 1; assert(iy >= iyb); } + if (ix < 0 || iy < 0 || ix >= (int64_t)m_cols || iy >= (int64_t)m_rows) + return; if (! visitor(iy, ix)) return; } while (ix != ixb || iy != iyb); @@ -270,6 +274,8 @@ public: iy += 1; assert(iy <= iyb); } + if (ix < 0 || iy < 0 || ix >= (int64_t)m_cols || iy >= (int64_t)m_rows) + return; if (! visitor(iy, ix)) return; } while (ix != ixb || iy != iyb); @@ -307,6 +313,8 @@ public: iy -= 1; assert(iy >= iyb); } + if (ix < 0 || iy < 0 || ix >= (int64_t)m_cols || iy >= (int64_t)m_rows) + return; if (! visitor(iy, ix)) return; } while (ix != ixb || iy != iyb); From 208ebfc70316c6e540fcd9c2b00c960c10c4230d Mon Sep 17 00:00:00 2001 From: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> Date: Tue, 7 Apr 2026 11:24:46 -0300 Subject: [PATCH 5/9] Combine brims option (Prusa style brims) (#12343) * Union ex brims Revert "Union ex brims" This reverts commit bbc9a39faf318dc2df093eb2bdcebf19a4162fe9. Update Brim.cpp * dont repeat paths * Update Brim.cpp * multimaterial brim independiente * Normal brim if is by object * fix print order * cleaning 1 * cleaning 2 * Normal brim if multimaterial on first layer * fix artifact * combine_brim optional refactoring * refactoring gcode.cpp * refactoring brim.cpp Update Brim.cpp * Remove multimaterial first-layer check for brims Stop detecting extruders used on the first layer and remove the is_multimaterial_first_layer guard. Simplify can_combine_brims to only consider combine_brims and whether printing is ByObject, allowing brims to be combined across extruders unless printing by object or combine_brims is disabled. Cleans up unused code and simplifies brim-generation conditions. * Remove material specification from unified brim comment * Update PrintConfig.cpp --- src/libslic3r/Brim.cpp | 67 +++++++++++++++++++++++---- src/libslic3r/GCode.cpp | 33 +++++++++++++ src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 7 +++ src/libslic3r/PrintConfig.hpp | 1 + src/slic3r/GUI/ConfigManipulation.cpp | 1 + src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 1 + 8 files changed, 102 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index 35d971a465..8b245e82c5 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -928,16 +928,63 @@ void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_ for (size_t iia = 0; iia < islands_area.size(); ++iia) islands_area[iia].translate(plate_shift); - for (auto iter = brimAreaMap.begin(); iter != brimAreaMap.end(); ++iter) { - if (!iter->second.empty()) { - brimMap.insert(std::make_pair(iter->first, makeBrimInfill(iter->second, print, islands_area))); - }; - } - for (auto iter = supportBrimAreaMap.begin(); iter != supportBrimAreaMap.end(); ++iter) { - if (!iter->second.empty()) { - supportBrimMap.insert(std::make_pair(iter->first, makeBrimInfill(iter->second, print, islands_area))); - }; + const bool combine_brims = print.config().combine_brims.value; + const bool is_by_object = (print.config().print_sequence == PrintSequence::ByObject); + const bool can_combine_brims = combine_brims && !is_by_object; + + if (!can_combine_brims) { + // Orca: Generate brims separately for each object when multiple extruders are used + for (auto iter = brimAreaMap.begin(); iter != brimAreaMap.end(); ++iter) { + if (!iter->second.empty()) { + brimMap.insert(std::make_pair(iter->first, makeBrimInfill(iter->second, print, islands_area))); + }; + } + for (auto iter = supportBrimAreaMap.begin(); iter != supportBrimAreaMap.end(); ++iter) { + if (!iter->second.empty()) { + supportBrimMap.insert(std::make_pair(iter->first, makeBrimInfill(iter->second, print, islands_area))); + }; + } + } else { + // Orca: Unified brim mode (non-sequential printing) + ExPolygons all_brims_merged; + std::vector brim_object_ids; + + // Add all object brims + for (auto& [obj_id, brims] : brimAreaMap) { + if (!brims.empty()) { + expolygons_append(all_brims_merged, brims); + brim_object_ids.push_back(obj_id); + } + } + + if (!all_brims_merged.empty()) { + // Merge all brims into a single continuous area + all_brims_merged = union_ex(all_brims_merged); + + // Apply a tiny morphological cleanup to reduce boolean-union micro-artifacts. + const float brim_cleanup_delta = std::max(float(scaled_resolution), float(SCALED_EPSILON)); + all_brims_merged = offset2_ex(all_brims_merged, brim_cleanup_delta, -brim_cleanup_delta, jtRound, scaled_resolution); + + // Generate infill once for the merged brim area. + ExtrusionEntityCollection merged_brim = makeBrimInfill(all_brims_merged, print, islands_area); + + // In unified mode, assign the merged brim to a deterministic carrier object. + // Pick the first object in print order that actually contributed brim area. + ObjectID carrier_id; + bool carrier_found = false; + for (const auto& [obj_id, _extruder] : objPrintVec) { + if (std::find(brim_object_ids.begin(), brim_object_ids.end(), obj_id) != brim_object_ids.end()) { + carrier_id = obj_id; + carrier_found = true; + break; + } + } + + if (!carrier_found) + carrier_id = brim_object_ids.front(); + + brimMap[carrier_id] = std::move(merged_brim); + } } } - } // namespace Slic3r diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index fe10cbcd41..ba47fac081 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -4993,6 +4993,39 @@ LayerResult GCode::process_layer( bool has_insert_wrapping_detection_gcode = false; // Extrude the skirt, brim, support, perimeters, infill ordered by the extruders. + // Orca: Print unified global brim before any object. + // Only do this if `combine_brims` is enabled and we are printing by layer. + if (first_layer && sequence_by_layer && m_config.combine_brims && !print.m_brimMap.empty()) { + const ObjectID unified_object_id = [&]() -> ObjectID { + ObjectID id; + bool found = false; + for (const auto& [obj_id, brim] : print.m_brimMap) { + const bool has_printable_entities = std::any_of(brim.entities.begin(), brim.entities.end(), + [](const ExtrusionEntity* ee) { return ee != nullptr; }); + if (!has_printable_entities) + continue; + if (found) + return ObjectID(); + id = obj_id; + found = true; + } + return found ? id : ObjectID(); + }(); + + if (unified_object_id.valid()) { + const auto it = print.m_brimMap.find(unified_object_id); + if (it != print.m_brimMap.end()) { + this->set_origin(0., 0.); + for (const ExtrusionEntity* ee : it->second.entities) + if (ee != nullptr) + gcode += this->extrude_entity(*ee, "brim", m_config.support_speed.value); + + // Mark brim as printed for this object to avoid per-object brim emission later. + this->m_objsWithBrim.erase(unified_object_id); + } + } + } + for (unsigned int extruder_id : layer_tools.extruders) { if (print.config().skirt_type == stCombined && !print.skirt().empty()) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 826d8a1a19..f8001d2806 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -904,7 +904,7 @@ static std::vector s_Preset_print_options { "top_surface_speed", "support_speed", "support_object_xy_distance", "support_object_first_layer_gap", "support_interface_speed", "bridge_speed", "internal_bridge_speed", "gap_infill_speed", "travel_speed", "travel_speed_z", "initial_layer_speed", "outer_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", "default_acceleration", "skirt_type", "skirt_loops", "skirt_speed","min_skirt_length", "skirt_distance", "skirt_start_angle", "skirt_height","single_loop_draft_shield", "draft_shield", - "brim_width", "brim_object_gap", "brim_use_efc_outline", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "support_threshold_overlap","enforce_support_layers", + "brim_width", "brim_object_gap", "brim_use_efc_outline", "combine_brims", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "support_threshold_overlap","enforce_support_layers", "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion", "support_base_pattern", "support_base_pattern_spacing", "support_expansion", "support_style", // BBS diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index c4b3c28ad9..d609fdb454 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1594,6 +1594,13 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(false)); + def = this->add("combine_brims", coBool); + def->label = L("Combine brims"); + def->category = L("Support"); + def->tooltip = L("Combine multiple brims into one when they are close to each other. This can improve brim adhesion."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + def = this->add("brim_ears", coBool); def->label = L("Brim ears"); def->category = L("Support"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 54c26c312a..425603bd08 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1550,6 +1550,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionString, thumbnails)) // BBS: move from PrintObjectConfig ((ConfigOptionBool, independent_support_layer_height)) + ((ConfigOptionBool, combine_brims)) // SoftFever ((ConfigOptionPercents, filament_shrink)) ((ConfigOptionPercents, filament_shrinkage_compensation_z)) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index f3f98a6fad..377f6f5ae6 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -706,6 +706,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co bool have_brim = (config->opt_enum("brim_type") != btNoBrim); toggle_field("brim_object_gap", have_brim); toggle_field("brim_use_efc_outline", have_brim); + toggle_field("combine_brims", have_brim); bool have_brim_width = (config->opt_enum("brim_type") != btNoBrim) && config->opt_enum("brim_type") != btAutoBrim && config->opt_enum("brim_type") != btPainted; toggle_field("brim_width", have_brim_width); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 64274adf19..86144a227f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4809,7 +4809,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod", "nozzle_height", "skirt_type", "skirt_loops", "skirt_speed","min_skirt_length", "skirt_distance", "skirt_start_angle", - "brim_width", "brim_object_gap", "brim_use_efc_outline", "brim_type", "nozzle_diameter", "single_extruder_multi_material", "preferred_orientation", + "brim_width", "brim_object_gap", "brim_use_efc_outline", "combine_brims", "brim_type", "nozzle_diameter", "single_extruder_multi_material", "preferred_orientation", "enable_prime_tower", "wipe_tower_x", "wipe_tower_y", "prime_tower_width", "prime_tower_brim_width", "prime_tower_skip_points", "prime_tower_enable_framework", "prime_tower_infill_gap", "prime_volume", "extruder_colour", "filament_colour", "filament_type", "material_colour", "printable_height", "extruder_printable_height", "printer_model", "printer_technology", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index c94fd1c7a4..c2cac5519f 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2658,6 +2658,7 @@ void TabPrint::build() optgroup->append_single_option_line("brim_width", "others_settings_brim#width"); optgroup->append_single_option_line("brim_object_gap", "others_settings_brim#brim-object-gap"); optgroup->append_single_option_line("brim_use_efc_outline", "others_settings_brim#brim-use-efc-outline"); + optgroup->append_single_option_line("combine_brims", "others_settings_brim#combine-brim"); optgroup->append_single_option_line("brim_ears_max_angle", "others_settings_brim#ear-max-angle"); optgroup->append_single_option_line("brim_ears_detection_length", "others_settings_brim#ear-detection-radius"); From 6975b5d84cd2000365237e1cbb384ab40b8f47ba Mon Sep 17 00:00:00 2001 From: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> Date: Tue, 7 Apr 2026 11:25:28 -0300 Subject: [PATCH 6/9] =?UTF-8?q?=F0=9F=92=A5Fix=20crash=20-=20inconsistent?= =?UTF-8?q?=20draft=20shield=20layer=20height=20(#12937)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix crash-inconsistent draft shield * Skip empty skirt G-code and fix skirt_done handling Track whether a skirt was actually generated for the current print Z and avoid appending empty skirt G-code. Only pop m_skirt_done when a skirt was emitted to prevent corrupting skirt state across multiple instances per layer. Also use a temporary string (moved into the main gcode) to detect emptiness before updating state. Preserves existing first-layer clearing behavior. --- src/libslic3r/GCode.cpp | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ba47fac081..ed7f153ab3 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -5137,19 +5137,37 @@ LayerResult GCode::process_layer( if (is_anything_overridden && print_wipe_extrusions == 0) gcode+="; PURGING FINISHED\n"; + bool skirt_generated_for_current_print_z = false; + for (InstanceToPrint &instance_to_print : instances_to_print) { if (print.config().skirt_type == stPerObject && !instance_to_print.print_object.object_skirt().empty() && - print.config().print_sequence == PrintSequence::ByLayer - && - (layer.id() < print.config().skirt_height || print.config().draft_shield == DraftShield::dsEnabled)) + print.config().print_sequence == PrintSequence::ByLayer) { - if (first_layer) - m_skirt_done.clear(); - const Point& offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift; - gcode += generate_skirt(print, instance_to_print.print_object.object_skirt(), offset, instance_to_print.print_object.config().skirt_start_angle, layer_tools, layer, extruder_id); - if (instances_to_print.size() > 1 && &instance_to_print != &*(instances_to_print.end() - 1)) - m_skirt_done.pop_back(); + const LayerToPrint& layer_to_print = layers[instance_to_print.layer_id]; + const Layer* skirt_layer = layer_to_print.object_layer; + if (skirt_layer == nullptr && layer_to_print.support_layer != nullptr && + layer_to_print.support_layer->id() < layer_to_print.support_layer->object()->slicing_parameters().raft_layers()) { + skirt_layer = layer_to_print.support_layer; + } + + if (skirt_layer != nullptr && + (skirt_layer->id() < print.config().skirt_height || print.config().draft_shield == DraftShield::dsEnabled)) { + const bool skirt_first_layer = (skirt_layer->id() == 0 && std::abs(skirt_layer->bottom_z()) < EPSILON); + if (skirt_first_layer) + m_skirt_done.clear(); + + if (skirt_generated_for_current_print_z && !m_skirt_done.empty()) + m_skirt_done.pop_back(); + + const Point& offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift; + std::string skirt_gcode = generate_skirt(print, instance_to_print.print_object.object_skirt(), offset, + instance_to_print.print_object.config().skirt_start_angle, layer_tools, + *skirt_layer, extruder_id); + if (!skirt_gcode.empty()) + skirt_generated_for_current_print_z = true; + gcode += std::move(skirt_gcode); + } } const auto& inst = instance_to_print.print_object.instances()[instance_to_print.instance_id]; From e1d6cb1764e167d55781601f9dc807a3ed5212ee Mon Sep 17 00:00:00 2001 From: Vovodroid Date: Tue, 7 Apr 2026 17:33:32 +0300 Subject: [PATCH 7/9] Remove "auto" wall direction option (#6193) Remove "auto" wall direction --- src/libslic3r/GCode.cpp | 7 ++++-- src/libslic3r/PerimeterGenerator.cpp | 32 ++++++++++++++------------- src/libslic3r/PrintConfig.cpp | 8 +++---- src/libslic3r/PrintConfig.hpp | 1 - src/slic3r/GUI/ConfigManipulation.cpp | 7 +----- src/slic3r/GUI/PartPlate.cpp | 1 - 6 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ed7f153ab3..04a2df48db 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -5600,7 +5600,10 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou if (m_config.spiral_mode && !is_hole) { // if spiral vase, we have to ensure that all contour are in the same orientation. - loop.make_counter_clockwise(); + if (m_config.wall_direction == WallDirection::CounterClockwise) + loop.make_counter_clockwise(); + else + loop.make_clockwise(); } //if (loop.loop_role() == elrSkirt && (this->m_layer->id() % 2 == 1)) // loop.reverse(); @@ -5662,7 +5665,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // 1 - the currently printed external perimeter and 2 - the neighbouring internal perimeter. if (m_config.wipe_before_external_loop.value && !paths.empty() && paths.front().size() > 1 && paths.back().size() > 1 && paths.front().role() == erExternalPerimeter && region_perimeters.size() > 1) { const bool is_full_loop_ccw = loop.polygon().is_counter_clockwise(); - bool is_hole_loop = (loop.loop_role() & ExtrusionLoopRole::elrHole) != 0; // loop.make_counter_clockwise(); + bool is_hole_loop = (loop.loop_role() & ExtrusionLoopRole::elrHole) != 0; const double nozzle_diam = nozzle_diameter; // note: previous & next are inverted to extrude "in the opposite direction, and we are "rewinding" diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index cc055c9d8a..7da2f20725 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -253,7 +253,10 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime ExtrusionLoop *eloop = static_cast(coll.entities[idx.first]); coll.entities[idx.first] = nullptr; - eloop->make_counter_clockwise(); + if (perimeter_generator.config->wall_direction == WallDirection::CounterClockwise) + eloop->make_counter_clockwise(); + else + eloop->make_clockwise(); eloop->inset_idx = loop.depth; if (loop.is_contour) { out.append(std::move(children.entities)); @@ -511,7 +514,10 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p if (!paths.empty()) { if (extrusion->is_closed) { ExtrusionLoop extrusion_loop(std::move(paths), pg_extrusion.is_contour ? elrDefault : elrHole); - extrusion_loop.make_counter_clockwise(); + if (perimeter_generator.config->wall_direction == WallDirection::CounterClockwise) + extrusion_loop.make_counter_clockwise(); + else + extrusion_loop.make_clockwise(); // TODO: it seems in practice that ExtrusionLoops occasionally have significantly disconnected paths, // triggering the asserts below. Is this a problem? for (auto it = std::next(extrusion_loop.paths.begin()); it != extrusion_loop.paths.end(); ++it) { @@ -1106,7 +1112,7 @@ static void reorient_perimeters(ExtrusionEntityCollection &entities, bool steep_ } if (need_reverse && !isExternal) { - eloop->make_clockwise(); + eloop->reverse(); } } } @@ -1414,18 +1420,17 @@ void PerimeterGenerator::process_classic() // at this point, all loops should be in contours[0] bool steep_overhang_contour = false; bool steep_overhang_hole = false; - const WallDirection wall_direction = config->wall_direction; - if (wall_direction != WallDirection::Auto) { - // Skip steep overhang detection if wall direction is specified + if (!config->overhang_reverse) { + // Skip steep overhang detection no reverse is specified steep_overhang_contour = true; steep_overhang_hole = true; } ExtrusionEntityCollection entities = traverse_loops(*this, contours.front(), thin_walls, steep_overhang_contour, steep_overhang_hole); // All walls are counter-clockwise initially, so we don't need to reorient it if that's what we want - if (wall_direction != WallDirection::CounterClockwise) { + if (config->overhang_reverse) { reorient_perimeters(entities, steep_overhang_contour, steep_overhang_hole, // Reverse internal only if the wall direction is auto - this->config->overhang_reverse_internal_only && wall_direction == WallDirection::Auto); + this->config->overhang_reverse_internal_only); } // if brim will be printed, reverse the order of perimeters so that @@ -2444,18 +2449,15 @@ void PerimeterGenerator::process_arachne() bool steep_overhang_contour = false; bool steep_overhang_hole = false; - const WallDirection wall_direction = config->wall_direction; - if (wall_direction != WallDirection::Auto) { - // Skip steep overhang detection if wall direction is specified + if (!config->overhang_reverse) { + // Skip steep overhang detection no reverse is specified steep_overhang_contour = true; steep_overhang_hole = true; } if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions, steep_overhang_contour, steep_overhang_hole); !extrusion_coll.empty()) { - // All walls are counter-clockwise initially, so we don't need to reorient it if that's what we want - if (wall_direction != WallDirection::CounterClockwise) { + if (config->overhang_reverse) { reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole, - // Reverse internal only if the wall direction is auto - this->config->overhang_reverse_internal_only && wall_direction == WallDirection::Auto); + this->config->overhang_reverse_internal_only); } this->loops->append(extrusion_coll); } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d609fdb454..d83a0aeba3 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -262,7 +262,6 @@ CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(WallSequence) //Orca static t_config_enum_values s_keys_map_WallDirection{ - { "auto", int(WallDirection::Auto) }, { "ccw", int(WallDirection::CounterClockwise) }, { "cw", int(WallDirection::Clockwise)}, }; @@ -2028,14 +2027,12 @@ void PrintConfigDef::init_fff_params() def->category = L("Quality"); def->tooltip = L("The direction which the wall loops are extruded when looking down from the top.\n\nBy default all walls are extruded in counter-clockwise, unless Reverse on even is enabled. Set this to any option other than Auto will force the wall direction regardless of the Reverse on even.\n\nThis option will be disabled if spiral vase mode is enabled."); def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("auto"); def->enum_values.push_back("ccw"); def->enum_values.push_back("cw"); - def->enum_labels.push_back(L("Auto")); def->enum_labels.push_back(L("Counter clockwise")); def->enum_labels.push_back(L("Clockwise")); def->mode = comAdvanced; - def->set_default_value(new ConfigOptionEnum(WallDirection::Auto)); + def->set_default_value(new ConfigOptionEnum(WallDirection::CounterClockwise)); def = this->add("extruder", coInt); def->gui_type = ConfigOptionDef::GUIType::i_enum_open; @@ -7692,6 +7689,9 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va } else if (opt_key == "machine_switch_extruder_time") { opt_key = "machine_tool_change_time"; } + else if (opt_key == "wall_direction" && value == "auto") { + value = "ccw"; + } // Ignore the following obsolete configuration keys: static std::set ignore = { diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 425603bd08..7d40ef0706 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -126,7 +126,6 @@ enum class WallSequence { // Orca enum class WallDirection { - Auto, CounterClockwise, Clockwise, Count, diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 377f6f5ae6..e41886008b 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -327,7 +327,6 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con config->opt_int("enforce_support_layers") == 0 && ! config->opt_bool("detect_thin_wall") && ! config->opt_bool("overhang_reverse") && - config->opt_enum("wall_direction") == WallDirection::Auto && config->opt_enum("timelapse_type") == TimelapseType::tlTraditional && !config->opt_bool("enable_wrapping_detection"))) { @@ -342,7 +341,6 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con new_conf.set_key_value("enforce_support_layers", new ConfigOptionInt(0)); new_conf.set_key_value("detect_thin_wall", new ConfigOptionBool(false)); new_conf.set_key_value("overhang_reverse", new ConfigOptionBool(false)); - new_conf.set_key_value("wall_direction", new ConfigOptionEnum(WallDirection::Auto)); new_conf.set_key_value("timelapse_type", new ConfigOptionEnum(tlTraditional)); new_conf.set_key_value("enable_wrapping_detection", new ConfigOptionBool(false)); sparse_infill_density = 0; @@ -661,8 +659,6 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co toggle_field("top_shell_thickness", ! has_spiral_vase && has_top_shell); toggle_field("bottom_shell_thickness", ! has_spiral_vase && has_bottom_shell); - toggle_field("wall_direction", !has_spiral_vase); - // Gap fill is newly allowed in between perimeter lines even for empty infill (see GH #1476). toggle_field("gap_infill_speed", have_perimeters); @@ -906,8 +902,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co bool has_detect_overhang_wall = config->opt_bool("detect_overhang_wall"); bool has_overhang_reverse = config->opt_bool("overhang_reverse"); - bool force_wall_direction = config->opt_enum("wall_direction") != WallDirection::Auto; - bool allow_overhang_reverse = !has_spiral_vase && !force_wall_direction; + bool allow_overhang_reverse = !has_spiral_vase; toggle_line("overhang_reverse", allow_overhang_reverse); toggle_line("overhang_reverse_internal_only", allow_overhang_reverse && has_overhang_reverse); bool has_overhang_reverse_internal_only = config->opt_bool("overhang_reverse_internal_only"); diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index 98c285f46a..37baa25a5f 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -2812,7 +2812,6 @@ void PartPlate::set_vase_mode_related_object_config(int obj_id) { new_conf.set_key_value("detect_thin_wall", new ConfigOptionBool(false)); new_conf.set_key_value("timelapse_type", new ConfigOptionEnum(tlTraditional)); new_conf.set_key_value("overhang_reverse", new ConfigOptionBool(false)); - new_conf.set_key_value("wall_direction", new ConfigOptionEnum(WallDirection::Auto)); auto applying_keys = global_config->diff(new_conf); for (ModelObject* object : obj_ptrs) { From 4d1efad5e9f18cb72b67b28f64dfe87796f78d8b Mon Sep 17 00:00:00 2001 From: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> Date: Tue, 7 Apr 2026 12:46:39 -0300 Subject: [PATCH 8/9] Inverse print direction for holes (#12669) * Remove "auto" wall direction * hole direction reverse Co-Authored-By: Ian Bassi <12130714+ianalexis@users.noreply.github.com> Co-Authored-By: discip <53649486+discip@users.noreply.github.com> * Update description Co-Authored-By: Ian Bassi <12130714+ianalexis@users.noreply.github.com> * Improve description * XNOR logic * Classic wall generator XNOR * thin wall holes arachne * thin wall holes classic * better handling of thin wall holes arachne Co-Authored-By: Ioannis Giannakas <59056762+igiannakas@users.noreply.github.com> * Invert print order for thin wall holes * Reverse path order for thin wall holes under condition * avoid reverse Co-Authored-By: Ian Bassi <12130714+ianalexis@users.noreply.github.com> Co-Authored-By: Ian Bassi --------- Co-authored-by: vovodroid Co-authored-by: Ian Bassi <12130714+ianalexis@users.noreply.github.com> Co-authored-by: discip <53649486+discip@users.noreply.github.com> Co-authored-by: Ian Bassi Co-authored-by: Ioannis Giannakas <59056762+igiannakas@users.noreply.github.com> Co-authored-by: SoftFever --- src/libslic3r/PerimeterGenerator.cpp | 27 ++++++++++++++++++++------- src/libslic3r/PrintConfig.cpp | 2 +- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 7da2f20725..1fa4d58bb0 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -98,7 +98,7 @@ static bool detect_steep_overhang(const PrintRegionConfig *config, } static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perimeter_generator, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls, - bool &steep_overhang_contour, bool &steep_overhang_hole) + bool &steep_overhang_contour, bool &steep_overhang_hole, bool reverse_thin_wall_hole) { // loops is an arrayref of ::Loop objects // turn each one into an ExtrusionLoop object @@ -248,15 +248,24 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime } else { const PerimeterGeneratorLoop &loop = loops[idx.first]; assert(thin_walls.empty()); - ExtrusionEntityCollection children = traverse_loops(perimeter_generator, loop.children, thin_walls, steep_overhang_contour, steep_overhang_hole); + const bool reverse_children_thin_wall_hole = loops.size() == 1 && loop.is_contour && loop.children.size() == 1 && + (!loop.children.front().is_contour) && loop.children.front().children.empty(); + ExtrusionEntityCollection children = traverse_loops(perimeter_generator, loop.children, thin_walls, steep_overhang_contour, + steep_overhang_hole, reverse_children_thin_wall_hole); out.entities.reserve(out.entities.size() + children.entities.size() + 1); ExtrusionLoop *eloop = static_cast(coll.entities[idx.first]); coll.entities[idx.first] = nullptr; - if (perimeter_generator.config->wall_direction == WallDirection::CounterClockwise) + if ((perimeter_generator.config->wall_direction == WallDirection::CounterClockwise) == (loop.is_contour || reverse_thin_wall_hole)) eloop->make_counter_clockwise(); else eloop->make_clockwise(); + + // Orca: Reverse print order for thin wall holes. + if (reverse_thin_wall_hole) { + std::reverse(out.entities.begin(), out.entities.end()); + } + eloop->inset_idx = loop.depth; if (loop.is_contour) { out.append(std::move(children.entities)); @@ -514,10 +523,11 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p if (!paths.empty()) { if (extrusion->is_closed) { ExtrusionLoop extrusion_loop(std::move(paths), pg_extrusion.is_contour ? elrDefault : elrHole); - if (perimeter_generator.config->wall_direction == WallDirection::CounterClockwise) + if ((perimeter_generator.config->wall_direction == WallDirection::CounterClockwise) == + (pg_extrusion.is_contour || pg_extrusions.size() == 2)) extrusion_loop.make_counter_clockwise(); else - extrusion_loop.make_clockwise(); + extrusion_loop.make_clockwise(); // TODO: it seems in practice that ExtrusionLoops occasionally have significantly disconnected paths, // triggering the asserts below. Is this a problem? for (auto it = std::next(extrusion_loop.paths.begin()); it != extrusion_loop.paths.end(); ++it) { @@ -525,8 +535,11 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); } assert(extrusion_loop.paths.front().first_point() == extrusion_loop.paths.back().last_point()); - extrusion_coll.append(std::move(extrusion_loop)); + // Orca: Reverse the order of paths for thin wall holes. We define thin wall hole as a hole with only one perimeter. + const bool thin_wall_hole = !pg_extrusion.is_contour && pg_extrusions.size() == 2; + if (thin_wall_hole && perimeter_generator.config->wall_sequence != WallSequence::OuterInner) + std::reverse(extrusion_coll.entities.begin(), extrusion_coll.entities.end()); } else { // Because we are processing one ExtrusionLine all ExtrusionPaths should form one connected path. @@ -1425,7 +1438,7 @@ void PerimeterGenerator::process_classic() steep_overhang_contour = true; steep_overhang_hole = true; } - ExtrusionEntityCollection entities = traverse_loops(*this, contours.front(), thin_walls, steep_overhang_contour, steep_overhang_hole); + ExtrusionEntityCollection entities = traverse_loops(*this, contours.front(), thin_walls, steep_overhang_contour, steep_overhang_hole, false); // All walls are counter-clockwise initially, so we don't need to reorient it if that's what we want if (config->overhang_reverse) { reorient_perimeters(entities, steep_overhang_contour, steep_overhang_hole, diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d83a0aeba3..4428241e22 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2025,7 +2025,7 @@ void PrintConfigDef::init_fff_params() def = this->add("wall_direction", coEnum); def->label = L("Wall loop direction"); def->category = L("Quality"); - def->tooltip = L("The direction which the wall loops are extruded when looking down from the top.\n\nBy default all walls are extruded in counter-clockwise, unless Reverse on even is enabled. Set this to any option other than Auto will force the wall direction regardless of the Reverse on even.\n\nThis option will be disabled if spiral vase mode is enabled."); + def->tooltip = L("The direction which the contour wall loops are extruded when looking down from the top.\nHoles are printed in the opposite direction to the contour to maintain alignment with layers whose contour polygons are incomplete and change direction, also partially forming the contour of a hole.. \n\nThis option will be disabled if spiral vase mode is enabled."); def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("ccw"); def->enum_values.push_back("cw"); From f685891b6e8f2d17ea8f4df5469d7f214983fbef Mon Sep 17 00:00:00 2001 From: Ian Bassi Date: Tue, 7 Apr 2026 16:06:20 -0300 Subject: [PATCH 9/9] Fix: Wiki + Wall loop direction desc (#13143) * Combine brims wiki fix * Typo fix --- src/libslic3r/PrintConfig.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 4428241e22..d4711a0da9 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2025,7 +2025,7 @@ void PrintConfigDef::init_fff_params() def = this->add("wall_direction", coEnum); def->label = L("Wall loop direction"); def->category = L("Quality"); - def->tooltip = L("The direction which the contour wall loops are extruded when looking down from the top.\nHoles are printed in the opposite direction to the contour to maintain alignment with layers whose contour polygons are incomplete and change direction, also partially forming the contour of a hole.. \n\nThis option will be disabled if spiral vase mode is enabled."); + def->tooltip = L("The direction which the contour wall loops are extruded when looking down from the top.\nHoles are printed in the opposite direction to the contour to maintain alignment with layers whose contour polygons are incomplete and change direction, also partially forming the contour of a hole.\n\nThis option will be disabled if spiral vase mode is enabled."); def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("ccw"); def->enum_values.push_back("cw"); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index c2cac5519f..35c85b36c1 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2658,7 +2658,7 @@ void TabPrint::build() optgroup->append_single_option_line("brim_width", "others_settings_brim#width"); optgroup->append_single_option_line("brim_object_gap", "others_settings_brim#brim-object-gap"); optgroup->append_single_option_line("brim_use_efc_outline", "others_settings_brim#brim-use-efc-outline"); - optgroup->append_single_option_line("combine_brims", "others_settings_brim#combine-brim"); + optgroup->append_single_option_line("combine_brims", "others_settings_brim#combine-brims"); optgroup->append_single_option_line("brim_ears_max_angle", "others_settings_brim#ear-max-angle"); optgroup->append_single_option_line("brim_ears_detection_length", "others_settings_brim#ear-detection-radius");