Skip to content

Commit

Permalink
Simple brim ears impl (#1779)
Browse files Browse the repository at this point in the history
* First working brim ear impl, ported from SuperSlicer

* Make brim ears configurable

* Generate ears only if ear size > 0

* Fix `Polygon::convex_points` as well as brim ear max angle

* Fix another error in `Polygon::convex_points` and `Polygon::concave_points`

* Apply brim ears to inner brims as well

* tweak hide and disable condition a bit

---------

Co-authored-by: SoftFever <softfeverever@gmail.com>
  • Loading branch information
Noisyfox and SoftFever authored Aug 13, 2023
1 parent afe1030 commit f714e72
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 19 deletions.
98 changes: 83 additions & 15 deletions src/libslic3r/Brim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ObjectID, ExPolygons>& brimAreaMap,
Expand All @@ -809,6 +855,7 @@ static ExPolygons outer_inner_brim_area(const Print& print,
std::vector<unsigned int>& printExtruders)
{
unsigned int support_material_extruder = printExtruders.front() + 1;
Flow flow = print.brim_flow();

ExPolygons brim_area;
ExPolygons no_brim_area;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
Expand Down
4 changes: 2 additions & 2 deletions src/libslic3r/Polygon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<double>();
if (filter(v1, v2))
out.emplace_back(p2);
out.emplace_back(p1);
v1 = v2;
p1 = p2;
}
Expand All @@ -249,7 +249,7 @@ template<typename ConvexConcaveFilterFn>
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;
Expand Down
2 changes: 1 addition & 1 deletion src/libslic3r/Preset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ static std::vector<std::string> 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",
Expand Down
34 changes: 33 additions & 1 deletion src/libslic3r/PrintConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -866,11 +867,13 @@ void PrintConfigDef::init_fff_params()
"Auto means the brim width is analysed and calculated automatically.");
def->enum_keys_map = &ConfigOptionEnum<BrimType>::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"));
Expand All @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions src/libslic3r/PrintConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ enum SLAPillarConnectionMode {

enum BrimType {
btAutoBrim, // BBS
btEar, // Orca
btOuterOnly,
btInnerOnly,
btOuterAndInner,
Expand Down Expand Up @@ -640,6 +641,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, brim_object_gap))
((ConfigOptionEnum<BrimType>, 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))
Expand Down
2 changes: 2 additions & 0 deletions src/libslic3r/PrintObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions src/libslic3r/libslic3r.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions src/slic3r/GUI/ConfigManipulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<BrimType>("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
Expand Down
2 changes: 2 additions & 0 deletions src/slic3r/GUI/Tab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down

0 comments on commit f714e72

Please sign in to comment.