diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index ab880954e8..dacd19aaf0 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -801,6 +801,52 @@ double configBrimWidthByVolumeGroups(double adhension, double maxSpeed, const st return brim_width; } +// Generate ears +// Ported from SuperSlicer: https://github.com/supermerill/SuperSlicer/blob/45d0532845b63cd5cefe7de7dc4ef0e0ed7e030a/src/libslic3r/Brim.cpp#L1116 +static ExPolygons make_brim_ears(ExPolygons& obj_expoly, coord_t size_ear, coord_t ear_detection_length, + coordf_t brim_ears_max_angle, bool is_outer_brim) { + ExPolygons mouse_ears_ex; + if (size_ear <= 0) { + return mouse_ears_ex; + } + // Detect places to put ears + const coordf_t angle_threshold = (180 - brim_ears_max_angle) * PI / 180.0; + Points pt_ears; + for (ExPolygon &poly : obj_expoly) { + Polygon decimated_polygon = poly.contour; + if (ear_detection_length > 0) { + // decimate polygon + Points points = poly.contour.points; + points.push_back(points.front()); + points = MultiPoint::_douglas_peucker(points, ear_detection_length); + if (points.size() > 4) { // don't decimate if it's going to be below 4 points, as it's surely enough to fill everything anyway + points.erase(points.end() - 1); + decimated_polygon.points = points; + } + } + + append(pt_ears, is_outer_brim ? decimated_polygon.convex_points(angle_threshold) + : decimated_polygon.concave_points(angle_threshold)); + } + + // Then add ears + // create ear pattern + Polygon point_round; + for (size_t i = 0; i < POLY_SIDES; i++) { + double angle = (2.0 * PI * i) / POLY_SIDES; + point_round.points.emplace_back(size_ear * cos(angle), size_ear * sin(angle)); + } + + // create ears + for (Point &pt : pt_ears) { + mouse_ears_ex.emplace_back(); + mouse_ears_ex.back().contour = point_round; + mouse_ears_ex.back().contour.translate(pt); + } + + return mouse_ears_ex; +} + //BBS: create all brims static ExPolygons outer_inner_brim_area(const Print& print, const float no_brim_offset, std::map& brimAreaMap, @@ -809,6 +855,7 @@ static ExPolygons outer_inner_brim_area(const Print& print, std::vector& printExtruders) { unsigned int support_material_extruder = printExtruders.front() + 1; + Flow flow = print.brim_flow(); ExPolygons brim_area; ExPolygons no_brim_area; @@ -838,6 +885,11 @@ static ExPolygons outer_inner_brim_area(const Print& print, const float scaled_additional_brim_width = scale_(floor(5 / flowWidth / 2) * flowWidth * 2); const float scaled_half_min_adh_length = scale_(1.1); bool has_brim_auto = object->config().brim_type == btAutoBrim; + const bool use_brim_ears = object->config().brim_type == btEar; + const bool has_inner_brim = brim_type == btInnerOnly || brim_type == btOuterAndInner || use_brim_ears; + const bool has_outer_brim = brim_type == btOuterOnly || brim_type == btOuterAndInner || brim_type == btAutoBrim || use_brim_ears; + coord_t ear_detection_length = scale_(object->config().brim_ears_detection_length.value); + coordf_t brim_ears_max_angle = object->config().brim_ears_max_angle.value; ExPolygons brim_area_object; ExPolygons no_brim_area_object; @@ -894,22 +946,38 @@ static ExPolygons outer_inner_brim_area(const Print& print, Polygons ex_poly_holes_reversed = ex_poly.holes; polygons_reverse(ex_poly_holes_reversed); - if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) { + if (has_outer_brim) { // BBS: inner and outer boundary are offset from the same polygon incase of round off error. auto innerExpoly = offset_ex(ex_poly.contour, brim_offset, jtRound, SCALED_RESOLUTION); - append(brim_area_object, diff_ex(offset_ex(innerExpoly, brim_width_mod, jtRound, SCALED_RESOLUTION), innerExpoly)); + auto &clipExpoly = innerExpoly; + if (use_brim_ears) { + coord_t size_ear = (brim_width_mod - brim_offset - flow.scaled_spacing()); + append(brim_area_object, diff_ex(make_brim_ears(innerExpoly, size_ear, ear_detection_length, brim_ears_max_angle, true), clipExpoly)); + } else { + // Normal brims + append(brim_area_object, diff_ex(offset_ex(innerExpoly, brim_width_mod, jtRound, SCALED_RESOLUTION), clipExpoly)); + } } - if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner) { - append(brim_area_object, diff_ex(offset_ex(ex_poly_holes_reversed, -brim_offset), offset_ex(ex_poly_holes_reversed, -brim_width - brim_offset))); + if (has_inner_brim) { + auto outerExpoly = offset_ex(ex_poly_holes_reversed, -brim_offset); + auto clipExpoly = offset_ex(ex_poly_holes_reversed, -brim_width - brim_offset); + + if (use_brim_ears) { + coord_t size_ear = (brim_width - brim_offset - flow.scaled_spacing()); + append(brim_area_object, diff_ex(make_brim_ears(outerExpoly, size_ear, ear_detection_length, brim_ears_max_angle, false), clipExpoly)); + } else { + // Normal brims + append(brim_area_object, diff_ex(outerExpoly, clipExpoly)); + } } - if (brim_type != BrimType::btInnerOnly && brim_type != BrimType::btOuterAndInner) { + if (!has_inner_brim) { // BBS: brim should be apart from holes append(no_brim_area_object, diff_ex(ex_poly_holes_reversed, offset_ex(ex_poly_holes_reversed, -scale_(5.)))); } - if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim) + if (!has_outer_brim) append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly_holes_reversed)); - if (brim_type == BrimType::btNoBrim) + if (!has_inner_brim && !has_outer_brim) append(no_brim_area_object, offset_ex(ex_poly_holes_reversed, -no_brim_offset)); append(holes_object, ex_poly_holes_reversed); } @@ -941,10 +1009,10 @@ static ExPolygons outer_inner_brim_area(const Print& print, for (const Polygon& support_contour : object->support_layers().front()->support_fills.polygons_covered_by_spacing()) { // Brim will not be generated for supports /* - if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) { + if (has_outer_brim) { append(brim_area_support, diff_ex(offset_ex(support_contour, brim_width + brim_offset, jtRound, SCALED_RESOLUTION), offset_ex(support_contour, brim_offset))); } - if (brim_type != BrimType::btNoBrim) + if (has_inner_brim || has_outer_brim) append(no_brim_area_support, offset_ex(support_contour, 0)); */ no_brim_area_support.emplace_back(support_contour); @@ -959,18 +1027,18 @@ static ExPolygons outer_inner_brim_area(const Print& print, brim_width_mod = floor(brim_width_mod / scaled_flow_width / 2) * scaled_flow_width * 2; // Brim will not be generated for supports /* - if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) { + if (has_outer_brim) { append(brim_area_support, diff_ex(offset_ex(ex_poly.contour, brim_width_mod + brim_offset, jtRound, SCALED_RESOLUTION), offset_ex(ex_poly.contour, brim_offset))); } - if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner) + if (has_inner_brim) append(brim_area_support, diff_ex(offset_ex(ex_poly.holes, -brim_offset), offset_ex(ex_poly.holes, -brim_width - brim_offset))); */ - if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim) + if (!has_outer_brim) append(no_brim_area_support, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly.holes)); - if (brim_type == BrimType::btNoBrim) + if (!has_inner_brim && !has_outer_brim) append(no_brim_area_support, offset_ex(ex_poly.holes, -no_brim_offset)); append(holes_support, ex_poly.holes); - if (brim_type != BrimType::btNoBrim) + if (has_inner_brim || has_outer_brim) append(no_brim_area_support, offset_ex(ex_poly.contour, 0)); no_brim_area_support.emplace_back(ex_poly.contour); } diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 5254157917..6697af0af5 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -237,7 +237,7 @@ Points filter_points_by_vectors(const Points &poly, FilterFn filter) // p2 is next point to the currently visited point p1. Vec2d v2 = (p2 - p1).cast(); if (filter(v1, v2)) - out.emplace_back(p2); + out.emplace_back(p1); v1 = v2; p1 = p2; } @@ -249,7 +249,7 @@ template Points filter_convex_concave_points_by_angle_threshold(const Points &poly, double angle_threshold, ConvexConcaveFilterFn convex_concave_filter) { assert(angle_threshold >= 0.); - if (angle_threshold < EPSILON) { + if (angle_threshold > EPSILON) { double cos_angle = cos(angle_threshold); return filter_points_by_vectors(poly, [convex_concave_filter, cos_angle](const Vec2d &v1, const Vec2d &v2){ return convex_concave_filter(v1, v2) && v1.normalized().dot(v2.normalized()) < cos_angle; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 72ccd450da..74a0a7442e 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -752,7 +752,7 @@ static std::vector s_Preset_print_options { "top_surface_speed", "support_speed", "support_object_xy_distance", "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_loops", "skirt_speed", "skirt_distance", "skirt_height", "draft_shield", - "brim_width", "brim_object_gap", "brim_type", "enable_support", "support_type", "support_threshold_angle", "enforce_support_layers", + "brim_width", "brim_object_gap", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "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", "independent_support_layer_height", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index fd5b2dffc9..c8dc5cc5e3 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -240,7 +240,8 @@ static const t_config_enum_values s_keys_map_BrimType = { {"outer_only", btOuterOnly}, {"inner_only", btInnerOnly}, {"outer_and_inner", btOuterAndInner}, - {"auto_brim", btAutoBrim} // BBS + {"auto_brim", btAutoBrim}, // BBS + {"brim_ears", btEar}, // Orca }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BrimType) @@ -866,11 +867,13 @@ void PrintConfigDef::init_fff_params() "Auto means the brim width is analysed and calculated automatically."); def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.emplace_back("auto_brim"); + def->enum_values.emplace_back("brim_ears"); def->enum_values.emplace_back("outer_only"); def->enum_values.emplace_back("inner_only"); def->enum_values.emplace_back("outer_and_inner"); def->enum_values.emplace_back("no_brim"); def->enum_labels.emplace_back(L("Auto")); + def->enum_labels.emplace_back(L("Mouse ear")); def->enum_labels.emplace_back(L("outer_only")); def->enum_labels.emplace_back(L("Inner brim only")); def->enum_labels.emplace_back(L("Outer and inner brim")); @@ -888,6 +891,35 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(0.)); + def = this->add("brim_ears", coBool); + def->label = L("Brim ears"); + def->category = L("Support"); + def->tooltip = L("Only draw brim over the sharp edges of the model."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("brim_ears_max_angle", coFloat); + def->label = L("Brim ear max angle"); + def->category = L("Support"); + def->tooltip = L("Maximum angle to let a brim ear appear. \nIf set to 0, no brim will be created. \nIf set to " + "~180, brim will be created on everything but straight sections."); + def->sidetext = L("°"); + def->min = 0; + def->max = 180; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(125)); + + def = this->add("brim_ears_detection_length", coFloat); + def->label = L("Brim ear detection radius"); + def->category = L("Support"); + def->tooltip = L("The geometry will be decimated before dectecting sharp angles. This parameter indicates the " + "minimum length of the deviation for the decimation." + "\n0 to deactivate"); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(1)); + def = this->add("compatible_printers", coStrings); def->label = L("Compatible machine"); def->mode = comDevelop; diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 6dc52d892e..36d0358c5b 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -156,6 +156,7 @@ enum SLAPillarConnectionMode { enum BrimType { btAutoBrim, // BBS + btEar, // Orca btOuterOnly, btInnerOnly, btOuterAndInner, @@ -640,6 +641,8 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, brim_object_gap)) ((ConfigOptionEnum, brim_type)) ((ConfigOptionFloat, brim_width)) + ((ConfigOptionFloat, brim_ears_detection_length)) + ((ConfigOptionFloat, brim_ears_max_angle)) ((ConfigOptionBool, bridge_no_support)) ((ConfigOptionFloat, elefant_foot_compensation)) ((ConfigOptionFloat, max_bridge_length)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 0e4facc9d7..28e440460f 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -694,6 +694,8 @@ bool PrintObject::invalidate_state_by_config_options( if ( opt_key == "brim_width" || opt_key == "brim_object_gap" || opt_key == "brim_type" + || opt_key == "brim_ears_max_angle" + || opt_key == "brim_ears_detection_length" // BBS: brim generation depends on printing speed || opt_key == "outer_wall_speed" || opt_key == "small_perimeter_speed" diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 9cf0c959fa..ea218dced5 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -56,6 +56,8 @@ static constexpr double EPSILON = 1e-4; // int32_t fits an interval of (-2147.48mm, +2147.48mm) // with int64_t we don't have to worry anymore about the size of the int. static constexpr double SCALING_FACTOR = 0.000001; +// for creating circles (for brim_ear) +#define POLY_SIDES 24 static constexpr double PI = 3.141592653589793238; // When extruding a closed loop, the loop is interrupted and shortened a bit to reduce the seam. // SoftFever: replaced by seam_gap now diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index fdc62ec532..64f411fd8e 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -598,6 +598,15 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co // wall_filament uses the same logic as in Print::extruders() toggle_field("wall_filament", have_perimeters || have_brim); + bool have_brim_ear = (config->opt_enum("brim_type") == btEar); + const auto brim_width = config->opt_float("brim_width"); + // disable brim_ears_max_angle and brim_ears_detection_length if brim_width is 0 + toggle_field("brim_ears_max_angle", brim_width > 0.0f); + toggle_field("brim_ears_detection_length", brim_width > 0.0f); + // hide brim_ears_max_angle and brim_ears_detection_length if brim_ear is not selected + toggle_line("brim_ears_max_angle", have_brim_ear); + toggle_line("brim_ears_detection_length", have_brim_ear); + bool have_raft = config->opt_int("raft_layers") > 0; bool have_support_material = config->opt_bool("enable_support") || have_raft; // BBS diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 01dea10ce8..54258afaf0 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2049,6 +2049,8 @@ void TabPrint::build() optgroup->append_single_option_line("brim_type", "auto-brim"); optgroup->append_single_option_line("brim_width", "auto-brim#manual"); optgroup->append_single_option_line("brim_object_gap", "auto-brim#brim-object-gap"); + optgroup->append_single_option_line("brim_ears_max_angle"); + optgroup->append_single_option_line("brim_ears_detection_length"); optgroup = page->new_optgroup(L("Prime tower"), L"param_tower"); optgroup->append_single_option_line("enable_prime_tower");