Simple brim ears impl (#1779)
* 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>
This commit is contained in:
parent
afe1030b58
commit
f714e72faa
9 changed files with 136 additions and 18 deletions
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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<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"));
|
||||
|
@ -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;
|
||||
|
|
|
@ -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<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))
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in a new issue