diff --git a/resources/ui_layout/print.ui b/resources/ui_layout/print.ui index 981f69aad..fb75271c6 100644 --- a/resources/ui_layout/print.ui +++ b/resources/ui_layout/print.ui @@ -106,6 +106,7 @@ group:Modifying slices line:Convert round vertical holes to polyholes setting:label$_:hole_to_polyhole setting:hole_to_polyhole_threshold + setting:hole_to_polyhole_twisted end_line group:Other setting:clip_multipart_objects diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 776893b74..8ecd56c37 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -597,6 +597,7 @@ const std::vector& Preset::print_options() "hole_size_threshold", "hole_to_polyhole", "hole_to_polyhole_threshold", + "hole_to_polyhole_twisted", "threads", // wipe tower "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index f11ab4473..5f4190801 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4440,13 +4440,21 @@ void PrintConfigDef::init_fff_params() def->full_label = L("Polyhole detection margin"); def->category = OptionCategory::slicing; def->tooltip = L("Maximum defection of a point to the estimated radius of the circle." - "\nAs cylinders are often exported as triangles of varying size, points may not be on the circle circumference." - " This setting allows you some leway to broaden the detection." - "\nIn mm or in % of the radius."); + "\nAs cylinders are often exported as triangles of varying size, points may not be on the circle circumference." + " This setting allows you some leway to broaden the detection." + "\nIn mm or in % of the radius."); def->sidetext = L("mm or %"); def->mode = comExpert; def->set_default_value(new ConfigOptionFloatOrPercent(0.01, false)); + def = this->add("hole_to_polyhole_twisted", coBool); + def->label = L("Twisting"); + def->full_label = L("Polyhole twist"); + def->category = OptionCategory::slicing; + def->tooltip = L("Rotate the polyhole every layer."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionBool(true)); + def = this->add("z_offset", coFloat); def->label = L("Z offset"); def->category = OptionCategory::general; @@ -5596,6 +5604,7 @@ void PrintConfigDef::to_prusa(t_config_option_key& opt_key, std::string& value, "hole_size_threshold", "hole_to_polyhole", "hole_to_polyhole_threshold", +"hole_to_polyhole_twisted", "z_step", "milling_cutter", "milling_diameter", diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index cda0704f6..febe56cec 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -798,6 +798,7 @@ public: ConfigOptionFloatOrPercent infill_anchor_max; ConfigOptionBool hole_to_polyhole; ConfigOptionFloatOrPercent hole_to_polyhole_threshold; + ConfigOptionBool hole_to_polyhole_twisted; ConfigOptionInt infill_extruder; ConfigOptionFloatOrPercent infill_extrusion_width; ConfigOptionInt infill_every_layers; @@ -914,6 +915,7 @@ protected: OPT_PTR(infill_anchor_max); OPT_PTR(hole_to_polyhole); OPT_PTR(hole_to_polyhole_threshold); + OPT_PTR(hole_to_polyhole_twisted); OPT_PTR(infill_extruder); OPT_PTR(infill_extrusion_width); OPT_PTR(infill_every_layers); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 457931b7b..ce9b92c9d 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -151,25 +151,41 @@ namespace Slic3r { } - Polygon create_polyhole(const Point center, const coord_t radius, const coord_t nozzle_diameter) + + Polygons create_polyholes(const Point center, const coord_t radius, const coord_t nozzle_diameter, bool multiple) { // n = max(round(2 * d), 3); // for 0.4mm nozzle - size_t nb_polygons = (int)std::max(3, (int)std::round(4.0 * unscaled(radius) * 0.4 / unscaled(nozzle_diameter))); + size_t nb_edges = (int)std::max(3, (int)std::round(4.0 * unscaled(radius) * 0.4 / unscaled(nozzle_diameter))); // cylinder(h = h, r = d / cos (180 / n), $fn = n); - Points pts; - const float new_radius = radius / std::cos(PI / nb_polygons); - for (int i = 0; i < nb_polygons; ++i) { - float angle = (PI * 2 * i) / nb_polygons; - pts.emplace_back(center.x() + new_radius * cos(angle), center.y() + new_radius * sin(angle)); + //create x polyholes by rotation if multiple + int nb_polyhole = 1; + float rotation = 0; + if (multiple) { + nb_polyhole = 5; + rotation = 2 * float(PI) / (nb_edges * nb_polyhole); } - return Polygon{ pts }; + Polygons list; + for (int i_poly = 0; i_poly < nb_polyhole; i_poly++) + list.emplace_back(); + for (int i_poly = 0; i_poly < nb_polyhole; i_poly++) { + Polygon& pts = (((i_poly % 2) == 0) ? list[i_poly / 2] : list[(nb_polyhole + 1) / 2 + i_poly / 2]); + const float new_radius = radius / float(std::cos(PI / nb_edges)); + for (int i_edge = 0; i_edge < nb_edges; ++i_edge) { + float angle = rotation * i_poly + (float(PI) * 2 * i_edge) / nb_edges; + pts.points.emplace_back(center.x() + new_radius * cos(angle), center.y() + new_radius * sin(angle)); + } + pts.make_clockwise(); + } + //alternate + return list; } void PrintObject::_transform_hole_to_polyholes() { // get all circular holes for each layer // the id is center-diameter-extruderid - std::vector, Polygon*>>> layerid2center; + //the tuple is Point center; float diameter_max; int extruder_id; coord_t max_variation; bool twist; + std::vector, Polygon*>>> layerid2center; for (size_t i = 0; i < this->m_layers.size(); i++) layerid2center.emplace_back(); tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), @@ -196,9 +212,10 @@ namespace Slic3r { } // SCALED_EPSILON was a bit too harsh. Now using a config, as some may want some harsh setting and some don't. coord_t max_variation = std::max(SCALED_EPSILON, scale_(this->m_layers[layer_idx]->m_regions[region_idx]->region()->config().hole_to_polyhole_threshold.get_abs_value(unscaled(diameter_sum / hole.points.size())))); + bool twist = this->m_layers[layer_idx]->m_regions[region_idx]->region()->config().hole_to_polyhole_twisted.value; if (diameter_max - diameter_min < max_variation * 2) { layerid2center[layer_idx].emplace_back( - std::tuple{center, diameter_max, layer->m_regions[region_idx]->region()->config().perimeter_extruder.value, max_variation}, & hole); + std::tuple{center, diameter_max, layer->m_regions[region_idx]->region()->config().perimeter_extruder.value, max_variation, twist}, & hole); } } } @@ -209,7 +226,7 @@ namespace Slic3r { } }); //sort holes per center-diameter - std::map, std::vector>> id2layerz2hole; + std::map, std::vector>> id2layerz2hole; //search & find hole that span at least X layers const size_t min_nb_layers = 2; @@ -217,7 +234,7 @@ namespace Slic3r { for (size_t layer_idx = 0; layer_idx < this->m_layers.size(); ++layer_idx) { for (size_t hole_idx = 0; hole_idx < layerid2center[layer_idx].size(); ++hole_idx) { //get all other same polygons - std::tuple& id = layerid2center[layer_idx][hole_idx].first; + std::tuple& id = layerid2center[layer_idx][hole_idx].first; float max_z = layers()[layer_idx]->print_z; std::vector> holes; holes.emplace_back(layerid2center[layer_idx][hole_idx].second, layer_idx); @@ -225,7 +242,7 @@ namespace Slic3r { if (layers()[search_layer_idx]->print_z - layers()[search_layer_idx]->height - max_z > EPSILON) break; //search an other polygon with same id for (size_t search_hole_idx = 0; search_hole_idx < layerid2center[search_layer_idx].size(); ++search_hole_idx) { - std::tuple& search_id = layerid2center[search_layer_idx][search_hole_idx].first; + std::tuple& search_id = layerid2center[search_layer_idx][search_hole_idx].first; if (std::get<2>(id) == std::get<2>(search_id) && std::get<0>(id).distance_to(std::get<0>(search_id)) < std::get<3>(id) && std::abs(std::get<1>(id) - std::get<1>(search_id)) < std::get<3>(id) @@ -246,9 +263,9 @@ namespace Slic3r { } //create a polyhole per id and replace holes points by it. for (auto entry : id2layerz2hole) { - Polygon polyhole = create_polyhole(std::get<0>(entry.first), std::get<1>(entry.first), scale_(print()->config().nozzle_diameter.get_at(std::get<2>(entry.first) - 1))); - polyhole.make_clockwise(); + Polygons polyholes = create_polyholes(std::get<0>(entry.first), std::get<1>(entry.first), scale_(print()->config().nozzle_diameter.get_at(std::get<2>(entry.first) - 1)), std::get<4>(entry.first)); for (auto& poly_to_replace : entry.second) { + Polygon polyhole = polyholes[poly_to_replace.second % polyholes.size()]; //search the clone in layers->slices for (ExPolygon& explo_slice : m_layers[poly_to_replace.second]->lslices) { for (Polygon& poly_slice : explo_slice.holes) { diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 9fcf901e5..532c5d29b 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -430,7 +430,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) for (auto el : { "solid_fill_pattern", "infill_connection_solid" }) toggle_field(el, has_solid_infill); // should be top_solid_layers") > 1 || bottom_solid_layers") > 1 - toggle_field("hole_to_polyhole_threshold", config->opt_bool("hole_to_polyhole")); + for (auto el : { "hole_to_polyhole_threshold", "hole_to_polyhole_twisted" }) + toggle_field(el, config->opt_bool("hole_to_polyhole")); bool have_default_acceleration = config->option("default_acceleration")->value > 0; for (auto el : { "perimeter_acceleration", "infill_acceleration",