From b1b76c07235dbe55ea6f350c17e1e8143fb08643 Mon Sep 17 00:00:00 2001 From: supermerill Date: Wed, 9 Jan 2019 20:49:58 +0100 Subject: [PATCH] brim ears - add brim_ears setting: bool: set to true to activate this new funtino - add brim_ears_max_angle : float : max angle for a corner to be assigned a brim ear. --- src/libslic3r/GCode.cpp | 2 +- src/libslic3r/Print.cpp | 178 ++++++++++++++++++++++++++++++++-- src/libslic3r/Print.hpp | 1 + src/libslic3r/PrintConfig.cpp | 15 +++ src/libslic3r/PrintConfig.hpp | 4 + src/slic3r/GUI/Preset.cpp | 3 +- src/slic3r/GUI/Tab.cpp | 9 +- 7 files changed, 202 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index b3219008459..256a03862de 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1567,7 +1567,7 @@ void GCode::process_layer( this->set_origin(0., 0.); m_avoid_crossing_perimeters.use_external_mp = true; for (const ExtrusionEntity *ee : print.brim().entities) - gcode += this->extrude_loop(*dynamic_cast(ee), "brim", m_config.support_material_speed.value); + gcode += this->extrude_entity(*ee, "brim", m_config.support_material_speed.value); m_brim_done = true; m_avoid_crossing_perimeters.use_external_mp = false; // Allow a straight travel move to the first object point. diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 407ad562547..a4034981b3c 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -195,7 +195,9 @@ bool Print::invalidate_state_by_config_options(const std::vector 0) { this->set_status(88, "Generating brim"); - this->_make_brim(); + if (config().brim_ears) + this->_make_brim_ears(); + else + this->_make_brim(); } this->set_done(psBrim); } @@ -1673,8 +1678,7 @@ void Print::_make_skirt() m_skirt.reverse(); } -void Print::_make_brim() -{ +void Print::_make_brim() { // Brim is only printed on first layer and uses perimeter extruder. Flow flow = this->brim_flow(); Polygons islands; @@ -1682,7 +1686,7 @@ void Print::_make_brim() Polygons object_islands; for (ExPolygon &expoly : object->m_layers.front()->slices.expolygons) object_islands.push_back(expoly.contour); - if (! object->support_layers().empty()) + if (!object->support_layers().empty()) object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); islands.reserve(islands.size() + object_islands.size() * object->m_copies.size()); for (const Point &pt : object->m_copies) @@ -1693,7 +1697,7 @@ void Print::_make_brim() } Polygons loops; size_t num_loops = size_t(floor(m_config.brim_width.value / flow.spacing())); - for (size_t i = 0; i < num_loops; ++ i) { + for (size_t i = 0; i < num_loops; ++i) { this->throw_if_canceled(); islands = offset(islands, float(flow.scaled_spacing()), jtSquare); for (Polygon &poly : islands) { @@ -1705,12 +1709,172 @@ void Print::_make_brim() } polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing()))); } - + loops = union_pt_chained(loops, false); std::reverse(loops.begin(), loops.end()); extrusion_entities_append_loops(m_brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height())); } +void Print::_make_brim_ears() { + Flow flow = this->brim_flow(); + Points pt_ears; + Polygons islands; + for (PrintObject *object : m_objects) { + Polygons object_islands; + for (ExPolygon &expoly : object->m_layers.front()->slices.expolygons) + object_islands.push_back(expoly.contour); + if (!object->support_layers().empty()) + object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); + islands.reserve(islands.size() + object_islands.size() * object->m_copies.size()); + for (const Point ©_pt : object->m_copies) + for (const Polygon &poly : object_islands) { + islands.push_back(poly); + islands.back().translate(copy_pt); + for (const Point &p : poly.convex_points(config().brim_ears_max_angle.value * PI / 180.0)) { + pt_ears.push_back(p); + pt_ears.back() += (copy_pt); + } + } + } + + //create loops (same as standard brim) + Polygons loops; + size_t num_loops = size_t(floor(m_config.brim_width.value / flow.spacing())); + for (size_t i = 0; i < num_loops; ++i) { + this->throw_if_canceled(); + islands = offset(islands, float(flow.scaled_spacing()), jtSquare); + for (Polygon &poly : islands) { + // poly.simplify(SCALED_RESOLUTION); + poly.points.push_back(poly.points.front()); + Points p = MultiPoint::_douglas_peucker(poly.points, SCALED_RESOLUTION); + p.pop_back(); + poly.points = std::move(p); + } + polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing()))); + } + loops = union_pt_chained(loops, false); + + + //create ear pattern + coord_t size_ear = (scale_(m_config.brim_width.value) - flow.scaled_spacing()); + Polygon point_round; + point_round.points.push_back(Point(size_ear * 1, 0 * size_ear)); + point_round.points.push_back(Point(size_ear*0.966, 0.26*size_ear)); + point_round.points.push_back(Point(size_ear*0.87, 0.5*size_ear)); + point_round.points.push_back(Point(size_ear*0.7, 0.7*size_ear)); + point_round.points.push_back(Point(size_ear*0.5, 0.87*size_ear)); + point_round.points.push_back(Point(size_ear*0.26, 0.966*size_ear)); + point_round.points.push_back(Point(size_ear * 0, 1 * size_ear)); + point_round.points.push_back(Point(size_ear*-0.26, 0.966*size_ear)); + point_round.points.push_back(Point(size_ear*-0.5, 0.87*size_ear)); + point_round.points.push_back(Point(size_ear*-0.7, 0.7*size_ear)); + point_round.points.push_back(Point(size_ear*-0.87, 0.5*size_ear)); + point_round.points.push_back(Point(size_ear*-0.966, 0.26*size_ear)); + point_round.points.push_back(Point(size_ear*-1, 0 * size_ear)); + point_round.points.push_back(Point(size_ear*-0.966, -0.26*size_ear)); + point_round.points.push_back(Point(size_ear*-0.87, -0.5*size_ear)); + point_round.points.push_back(Point(size_ear*-0.7, -0.7*size_ear)); + point_round.points.push_back(Point(size_ear*-0.5, -0.87*size_ear)); + point_round.points.push_back(Point(size_ear*-0.26, -0.966*size_ear)); + point_round.points.push_back(Point(size_ear * 0, -1 * size_ear)); + point_round.points.push_back(Point(size_ear*0.26, -0.966*size_ear)); + point_round.points.push_back(Point(size_ear*0.5, -0.87*size_ear)); + point_round.points.push_back(Point(size_ear*0.7, -0.7*size_ear)); + point_round.points.push_back(Point(size_ear*0.87, -0.5*size_ear)); + point_round.points.push_back(Point(size_ear*0.966, -0.26*size_ear)); + + //create ears + Polygons mouse_ears; + for (Point pt : pt_ears) { + mouse_ears.push_back(point_round); + mouse_ears.back().translate(pt); + } + + //intersection + Polylines lines = intersection_pl(loops, mouse_ears); + + //reorder them + std::sort(lines.begin(), lines.end(), [](const Polyline &a, const Polyline &b)->bool { return a.closest_point(Point(0, 0))->y() < b.closest_point(Point(0, 0))->y(); }); + Polylines lines_sorted; + Polyline* previous = NULL; + Polyline* best = NULL; + double best_dist = -1; + size_t best_idx = 0; + while (lines.size() > 0) { + if (previous == NULL) { + lines_sorted.push_back(lines.back()); + previous = &lines_sorted.back(); + lines.erase(lines.end() - 1); + } else { + best = NULL; + best_dist = -1; + best_idx = 0; + for (size_t i = 0; i < lines.size(); ++i) { + Polyline &viewed_line = lines[i]; + double dist = viewed_line.points.front().distance_to(previous->points.front()); + dist = std::min(dist, viewed_line.points.front().distance_to(previous->points.back())); + dist = std::min(dist, viewed_line.points.back().distance_to(previous->points.front())); + dist = std::min(dist, viewed_line.points.back().distance_to(previous->points.back())); + if (dist < best_dist || best == NULL) { + best = &viewed_line; + best_dist = dist; + best_idx = i; + } + } + if (best != NULL) { + //copy new line inside the sorted array. + lines_sorted.push_back(lines[best_idx]); + lines.erase(lines.begin() + best_idx); + + //connect if near enough + if (lines_sorted.size() > 1) { + size_t idx = lines_sorted.size() - 2; + bool connect = false; + if (lines_sorted[idx].points.back().distance_to(lines_sorted[idx + 1].points.front()) < flow.scaled_spacing() * 2) { + connect = true; + } else if (lines_sorted[idx].points.back().distance_to(lines_sorted[idx + 1].points.back()) < flow.scaled_spacing() * 2) { + lines_sorted[idx + 1].reverse(); + connect = true; + } else if (lines_sorted[idx].points.front().distance_to(lines_sorted[idx + 1].points.front()) < flow.scaled_spacing() * 2) { + lines_sorted[idx].reverse(); + connect = true; + } else if (lines_sorted[idx].points.front().distance_to(lines_sorted[idx + 1].points.back()) < flow.scaled_spacing() * 2) { + lines_sorted[idx].reverse(); + lines_sorted[idx + 1].reverse(); + connect = true; + } + + if (connect) { + //connect them + lines_sorted[idx].points.insert( + lines_sorted[idx].points.end(), + lines_sorted[idx + 1].points.begin(), + lines_sorted[idx + 1].points.end()); + lines_sorted.erase(lines_sorted.begin() + idx + 1); + idx--; + } + } + + //update last position + previous = &lines_sorted.back(); + } else { + previous == NULL; + } + + } + } + + //push into extrusions + extrusion_entities_append_paths( + m_brim.entities, + lines_sorted, + erSkirt, + float(flow.mm3_per_mm()), + float(flow.width), + float(this->skirt_first_layer_height()) + ); +} + // Wipe tower support. bool Print::has_wipe_tower() const { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index de321557806..b68fb1518d6 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -374,6 +374,7 @@ class Print : public PrintBaseWithState void _make_skirt(); void _make_brim(); + void _make_brim_ears(); void _make_wipe_tower(); void _simplify_slices(double distance); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index f0d62bb8825..3b55e7ab3fb 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -212,6 +212,21 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->default_value = new ConfigOptionFloat(0); + def = this->add("brim_ears", coBool); + def->label = L(" "); + def->tooltip = L("Only draw brim over the sharp edges of the model."); + def->cli = "brim-ears!"; + def->default_value = new ConfigOptionBool(false); + + def = this->add("brim_ears_max_angle", coFloat); + def->label = L("max angle"); + def->tooltip = L("Maximum angle to let a brim ear appear."); + def->sidetext = L("°"); + def->cli = "brim-ears-max-angle=f"; + def->min = 0; + def->max = 180; + def->default_value = new ConfigOptionFloat(125); + def = this->add("clip_multipart_objects", coBool); def->label = L("Clip multi-part objects"); def->tooltip = L("When printing multi-material objects, this settings will make slic3r " diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index b5b290d3958..be1bc9f1ffc 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -788,6 +788,8 @@ class PrintConfig : public MachineEnvelopeConfig, public GCodeConfig ConfigOptionInts bridge_fan_speed; ConfigOptionInts top_fan_speed; ConfigOptionFloat brim_width; + ConfigOptionBool brim_ears; + ConfigOptionFloat brim_ears_max_angle; ConfigOptionBool complete_objects; ConfigOptionFloats colorprint_heights; ConfigOptionBools cooling; @@ -867,6 +869,8 @@ class PrintConfig : public MachineEnvelopeConfig, public GCodeConfig OPT_PTR(bridge_fan_speed); OPT_PTR(top_fan_speed); OPT_PTR(brim_width); + OPT_PTR(brim_ears); + OPT_PTR(brim_ears_max_angle); OPT_PTR(complete_objects); OPT_PTR(colorprint_heights); OPT_PTR(cooling); diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 876bb3b4593..84fae86b3ed 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -328,7 +328,8 @@ const std::vector& Preset::print_options() "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", "bridge_speed", "gap_fill", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", - "min_skirt_length", "brim_width", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers", + "min_skirt_length", "brim_width", "brim_ears", "brim_ears_max_angle", + "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers", "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 83f7e1e73cf..fa67a9b61a3 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -988,7 +988,11 @@ void TabPrint::build() optgroup->append_single_option_line("min_skirt_length"); optgroup = page->new_optgroup(_(L("Brim"))); - optgroup->append_single_option_line("brim_width"); + optgroup->append_single_option_line("brim_width"); + line = { _(L("Brim ears")), "" }; + line.append_option(optgroup->get_option("brim_ears")); + line.append_option(optgroup->get_option("brim_ears_max_angle")); + optgroup->append_line(line); page = add_options_page(_(L("Support material")), "building.png"); optgroup = page->new_optgroup(_(L("Support material"))); @@ -1379,6 +1383,9 @@ void TabPrint::update() // perimeter_extruder uses the same logic as in Print::extruders() get_field("perimeter_extruder")->toggle(have_perimeters || have_brim); + get_field("brim_ears")->toggle(have_brim); + get_field("brim_ears_max_angle")->toggle(have_brim && m_config->opt_bool("brim_ears")); + bool have_raft = m_config->opt_int("raft_layers") > 0; bool have_support_material = m_config->opt_bool("support_material") || have_raft; bool have_support_material_auto = have_support_material && m_config->opt_bool("support_material_auto");