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] 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");