diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index ba12a1edb..ff9e439ba 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -5,6 +5,7 @@ #include #include +#include namespace Slic3r { @@ -230,8 +231,128 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime return out; } +enum class FuzzyShape { + Triangle, + Sawtooth, + Random +}; + +static void fuzzy_polygon(Polygon &poly, FuzzyShape shape, double fuzzy_skin_thickness, double fuzzy_skin_point_dist) +{ +#if 0 + Point last = poly.points.at(poly.points.size() - 1); + Point last_processed = last; + + double max_length = scale_(2); + double min_length = scale_(1); + + if (poly.length() < scale_(5)) + return; + + deepness *= 3; + + bool triangle_or_sawtooth = shape == FuzzyShape::Sawtooth; + double length_sum = 0; + Points::iterator it = poly.points.begin(); + while (it != poly.points.end()) { + Point &pt = *it; + + Line line(last, pt); + double length = line.length(); + + // split long line + if (length > max_length) { + auto parts = int(ceil(length / max_length)); + if (parts == 2) { + Point point_to_insert(line.midpoint()); + it = poly.points.insert(it, point_to_insert); + } + else { + Vector part_vector = line.vector() / parts; + + Points points_to_insert; + Point point_to_insert(last); + while (--parts) { + point_to_insert += part_vector; + Point point_to_insert_2(point_to_insert); + points_to_insert.push_back(point_to_insert_2); + } + + it = poly.points.insert(it, points_to_insert.begin(), points_to_insert.end()); + } + continue; + } + + length_sum += length; + + // join short lines + if (length_sum < min_length) { + last = pt; + it = poly.points.erase(it); + continue; + } + + line = Line(last_processed, pt); + last = pt; + last_processed = pt; + + if (shape == FuzzyShape::Random) { + triangle_or_sawtooth = !(rand() % 2); + } + + Point point_to_insert(triangle_or_sawtooth ? pt : line.midpoint()); + + int scale = (rand() % deepness) + 1; + + Vec2d normal = line.normal().cast(); + normal /= line.length() / scale_(1.) / ((double)scale / 20.); + + it = poly.points.insert(it, point_to_insert + normal.cast()) + 2; + + length_sum = 0; + } + +#else + const double min_dist_between_points = fuzzy_skin_point_dist * 3. / 4.; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value + const double range_random_point_dist = fuzzy_skin_point_dist / 2.; + double dist_left_over = double(rand()) * (min_dist_between_points / 2) / double(RAND_MAX); // the distance to be traversed on the line before making the first new point + Point* p0 = &poly.points.back(); + Points out; + out.reserve(poly.points.size()); + for (Point &p1 : poly.points) + { // 'a' is the (next) new point between p0 and p1 + Vec2d p0p1 = (p1 - *p0).cast(); + double p0p1_size = p0p1.norm(); + // so that p0p1_size - dist_last_point evaulates to dist_left_over - p0p1_size + double dist_last_point = dist_left_over + p0p1_size * 2.; + for (double p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; + p0pa_dist += min_dist_between_points + double(rand()) * range_random_point_dist / double(RAND_MAX)) + { + double r = double(rand()) * (fuzzy_skin_thickness * 2.) / double(RAND_MAX) - fuzzy_skin_thickness; + out.emplace_back(*p0 + (p0p1 * (p0pa_dist / p0p1_size) + perp(p0p1).cast().normalized() * r).cast()); + dist_last_point = p0pa_dist; + } + dist_left_over = p0p1_size - dist_last_point; + p0 = &p1; + } + while (out.size() < 3) { + size_t point_idx = poly.size() - 2; + out.emplace_back(poly[point_idx]); + if (point_idx == 0) + break; + -- point_idx; + } + if (out.size() >= 3) + poly.points = std::move(out); +#endif +} + void PerimeterGenerator::process() { + // nasty hack! initialize random generator + auto time_us = std::chrono::duration_cast(std::chrono::time_point_cast(std::chrono::high_resolution_clock::now()).time_since_epoch()).count(); + srand(this->layer_id * time_us); + // other perimeters m_mm3_per_mm = this->perimeter_flow.mm3_per_mm(); coord_t perimeter_width = this->perimeter_flow.scaled_width(); @@ -272,6 +393,32 @@ void PerimeterGenerator::process() m_lower_slices_polygons = offset(*this->lower_slices, float(scale_(+nozzle_diameter/2))); } + // fuzzy skin configuration + double fuzzy_skin_thickness; + double fuzzy_skin_point_dist; + FuzzyShape fuzzy_skin_shape; + if (this->object_config->fuzzy_skin_perimeter_mode != FuzzySkinPerimeterMode::None) { + switch (this->object_config->fuzzy_skin_shape) { + case FuzzySkinShape::Triangle1: + case FuzzySkinShape::Triangle2: + case FuzzySkinShape::Triangle3: + fuzzy_skin_shape = FuzzyShape::Triangle; + break; + case FuzzySkinShape::Sawtooth1: + case FuzzySkinShape::Sawtooth2: + case FuzzySkinShape::Sawtooth3: + fuzzy_skin_shape = FuzzyShape::Sawtooth; + break; + case FuzzySkinShape::Random1: + case FuzzySkinShape::Random2: + case FuzzySkinShape::Random3: + fuzzy_skin_shape = FuzzyShape::Random; + break; + } + fuzzy_skin_thickness = scale_(this->object_config->fuzzy_skin_thickness); + fuzzy_skin_point_dist = scale_(this->object_config->fuzzy_skin_point_dist); + } + // we need to process each island separately because we might have different // extra perimeters for each one for (const Surface &surface : this->slices->surfaces) { @@ -352,13 +499,35 @@ void PerimeterGenerator::process() // If i > loop_number, we were looking just for gaps. break; } - for (const ExPolygon &expolygon : offsets) { + for (ExPolygon &expolygon : offsets) { // Outer contour may overlap with an inner contour, // inner contour may overlap with another inner contour, // outer contour may overlap with itself. //FIXME evaluate the overlaps, annotate each point with an overlap depth, - // compensate for the depth of intersection. - contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true)); + + bool skip_polygon = false; + + if (this->object_config->fuzzy_skin_perimeter_mode != FuzzySkinPerimeterMode::None) { + if (i == 0 && (this->object_config->fuzzy_skin_perimeter_mode != FuzzySkinPerimeterMode::ExternalSkipFirst || this->layer_id > 0)) { + if ( + this->object_config->fuzzy_skin_perimeter_mode == FuzzySkinPerimeterMode::External || + this->object_config->fuzzy_skin_perimeter_mode == FuzzySkinPerimeterMode::ExternalSkipFirst + ) { + ExPolygon expolygon_fuzzy(expolygon); + fuzzy_polygon(expolygon_fuzzy.contour, fuzzy_skin_shape, fuzzy_skin_thickness, fuzzy_skin_point_dist); + // compensate for the depth of intersection. + contours[i].emplace_back(PerimeterGeneratorLoop(expolygon_fuzzy.contour, i, true)); + skip_polygon = true; + } else + fuzzy_polygon(expolygon.contour, fuzzy_skin_shape, fuzzy_skin_thickness, fuzzy_skin_point_dist); + } + } + + if (!skip_polygon) { + // compensate for the depth of intersection. + contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true)); + } + if (! expolygon.holes.empty()) { holes[i].reserve(holes[i].size() + expolygon.holes.size()); for (const Polygon &hole : expolygon.holes) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index c24033df2..f75f653ef 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -411,6 +411,7 @@ const std::vector& Preset::print_options() "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing", "max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour", + "fuzzy_skin_perimeter_mode", "fuzzy_skin_shape", "fuzzy_skin_thickness", "fuzzy_skin_point_dist", #ifdef HAS_PRESSURE_EQUALIZER "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", #endif /* HAS_PRESSURE_EQUALIZER */ diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index da2b65568..9247e69da 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1007,6 +1007,68 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionInts { 0 }); + def = this->add("fuzzy_skin_perimeter_mode", coEnum); + def->label = L("Fuzzy skin perimeter mode"); + def->category = L("Fuzzy Skin"); + def->tooltip = L("Fuzzy skin perimeter mode."); + + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("none"); + def->enum_values.push_back("external_only"); + def->enum_values.push_back("external_only_skip_first_layer"); + def->enum_values.push_back("all"); + def->enum_labels.push_back(L("None")); + def->enum_labels.push_back(L("External")); + def->enum_labels.push_back(L("External (skip first layer)")); + def->enum_labels.push_back(L("All perimeters")); + def->mode = comSimple; + def->set_default_value(new ConfigOptionEnum(FuzzySkinPerimeterMode::None)); + + def = this->add("fuzzy_skin_shape", coEnum); + def->label = L("Fuzzy skin shape"); + def->category = L("Fuzzy Skin"); + def->tooltip = L("Fuzzy skin shape."); + + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("triangle1"); + def->enum_values.push_back("triangle2"); + def->enum_values.push_back("triangle3"); + def->enum_values.push_back("sawtooth1"); + def->enum_values.push_back("sawtooth2"); + def->enum_values.push_back("sawtooth3"); + def->enum_values.push_back("random1"); + def->enum_values.push_back("random2"); + def->enum_values.push_back("random3"); + def->enum_labels.push_back(L("Triangle (1)")); + def->enum_labels.push_back(L("Triangle (2)")); + def->enum_labels.push_back(L("Triangle (3)")); + def->enum_labels.push_back(L("Sawtooth (1)")); + def->enum_labels.push_back(L("Sawtooth (2)")); + def->enum_labels.push_back(L("Sawtooth (3)")); + def->enum_labels.push_back(L("Random (1)")); + def->enum_labels.push_back(L("Random (2)")); + def->enum_labels.push_back(L("Random (3)")); + def->mode = comSimple; + def->set_default_value(new ConfigOptionEnum(FuzzySkinShape::Triangle1)); + + def = this->add("fuzzy_skin_thickness", coFloat); + def->label = L("Fuzzy skin thickness"); + def->category = L("Fuzzy Skin"); + def->tooltip = L(""); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.3)); + + def = this->add("fuzzy_skin_point_dist", coFloat); + def->label = L("Fuzzy skin point distance"); + def->category = L("Fuzzy Skin"); + def->tooltip = L(""); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.8)); + def = this->add("gap_fill_speed", coFloat); def->label = L("Gap fill"); def->category = L("Speed"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index fa09cfb17..40929c34b 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -43,6 +43,25 @@ enum AuthorizationType { atKeyPassword, atUserPassword }; +enum class FuzzySkinPerimeterMode { + None, + External, + ExternalSkipFirst, + All +}; + +enum class FuzzySkinShape { + Triangle1, + Triangle2, + Triangle3, + Sawtooth1, + Sawtooth2, + Sawtooth3, + Random1, + Random2, + Random3 +}; + enum InfillPattern : int { ipRectilinear, ipMonotonic, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipCount, @@ -140,6 +159,33 @@ template<> inline const t_config_enum_values& ConfigOptionEnum inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { + static t_config_enum_values keys_map; + if (keys_map.empty()) { + keys_map["none"] = int(FuzzySkinPerimeterMode::None); + keys_map["external_only"] = int(FuzzySkinPerimeterMode::External); + keys_map["external_only_skip_first_layer"] = int(FuzzySkinPerimeterMode::ExternalSkipFirst); + keys_map["all"] = int(FuzzySkinPerimeterMode::All); + } + return keys_map; +} + +template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { + static t_config_enum_values keys_map; + if (keys_map.empty()) { + keys_map["triangle1"] = int(FuzzySkinShape::Triangle1); + keys_map["triangle2"] = int(FuzzySkinShape::Triangle2); + keys_map["triangle3"] = int(FuzzySkinShape::Triangle3); + keys_map["sawtooth1"] = int(FuzzySkinShape::Sawtooth1); + keys_map["sawtooth2"] = int(FuzzySkinShape::Sawtooth2); + keys_map["sawtooth3"] = int(FuzzySkinShape::Sawtooth3); + keys_map["random1"] = int(FuzzySkinShape::Random1); + keys_map["random2"] = int(FuzzySkinShape::Random2); + keys_map["random3"] = int(FuzzySkinShape::Random3); + } + return keys_map; +} + template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static t_config_enum_values keys_map; if (keys_map.empty()) { @@ -432,6 +478,10 @@ public: ConfigOptionFloat elefant_foot_compensation; ConfigOptionFloatOrPercent extrusion_width; ConfigOptionFloatOrPercent first_layer_height; + ConfigOptionEnum fuzzy_skin_perimeter_mode; + ConfigOptionEnum fuzzy_skin_shape; + ConfigOptionFloat fuzzy_skin_thickness; + ConfigOptionFloat fuzzy_skin_point_dist; ConfigOptionBool infill_only_where_needed; // Force the generation of solid shells between adjacent materials/volumes. ConfigOptionBool interface_shells; @@ -477,6 +527,10 @@ protected: OPT_PTR(elefant_foot_compensation); OPT_PTR(extrusion_width); OPT_PTR(first_layer_height); + OPT_PTR(fuzzy_skin_perimeter_mode); + OPT_PTR(fuzzy_skin_shape); + OPT_PTR(fuzzy_skin_thickness); + OPT_PTR(fuzzy_skin_point_dist); OPT_PTR(infill_only_where_needed); OPT_PTR(interface_shells); OPT_PTR(layer_height); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 99b4130f4..dcf94fe9a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -521,6 +521,10 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector(ret_enum); + else if (m_opt_id.compare("fuzzy_skin_perimeter_mode") == 0) + m_value = static_cast(ret_enum); + else if (m_opt_id.compare("fuzzy_skin_shape") == 0) + m_value = static_cast(ret_enum); else if (m_opt_id.compare("gcode_flavor") == 0) m_value = static_cast(ret_enum); else if (m_opt_id.compare("machine_limits_usage") == 0) diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index dea226012..6b664c0ed 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -182,6 +182,10 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if (opt_key.compare("ironing_type") == 0) config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); + else if (opt_key.compare("fuzzy_skin_perimeter_mode") == 0) + config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); + else if (opt_key.compare("fuzzy_skin_shape") == 0) + config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if (opt_key.compare("gcode_flavor") == 0) config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if (opt_key.compare("machine_limits_usage") == 0) diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 10774abe3..ced780adb 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -870,6 +870,12 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config else if (opt_key == "ironing_type") { ret = static_cast(config.option>(opt_key)->value); } + else if (opt_key == "fuzzy_skin_perimeter_mode") { + ret = static_cast(config.option>(opt_key)->value); + } + else if (opt_key == "fuzzy_skin_shape") { + ret = static_cast(config.option>(opt_key)->value); + } else if (opt_key == "gcode_flavor") { ret = static_cast(config.option>(opt_key)->value); } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index aec2f8754..974b709f7 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1434,6 +1434,18 @@ void TabPrint::build() optgroup->append_single_option_line("seam_position", category_path + "seam-position"); optgroup->append_single_option_line("external_perimeters_first", category_path + "external-perimeters-first"); + optgroup = page->new_optgroup(L("Fuzzy skin (experimental)")); + Option option = optgroup->get_option("fuzzy_skin_perimeter_mode"); + option.opt.width = 30; + optgroup->append_single_option_line(option); +#if 0 + option = optgroup->get_option("fuzzy_skin_shape"); + option.opt.width = 30; + optgroup->append_single_option_line(option); +#endif + optgroup->append_single_option_line(optgroup->get_option("fuzzy_skin_thickness")); + optgroup->append_single_option_line(optgroup->get_option("fuzzy_skin_point_dist")); + page = add_options_page(L("Infill"), "infill"); category_path = "infill_42#"; optgroup = page->new_optgroup(L("Infill")); @@ -1597,7 +1609,7 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Output file")); optgroup->append_single_option_line("gcode_comments"); optgroup->append_single_option_line("gcode_label_objects"); - Option option = optgroup->get_option("output_filename_format"); + option = optgroup->get_option("output_filename_format"); option.opt.full_width = true; optgroup->append_single_option_line(option);