#include #include "libslic3r/Algorithm/LineSegmentation/LineSegmentation.hpp" #include "libslic3r/Arachne/utils/ExtrusionJunction.hpp" #include "libslic3r/Arachne/utils/ExtrusionLine.hpp" #include "libslic3r/PerimeterGenerator.hpp" #include "libslic3r/Point.hpp" #include "libslic3r/Polygon.hpp" #include "libslic3r/PrintConfig.hpp" #include "FuzzySkin.hpp" using namespace Slic3r; namespace Slic3r::Feature::FuzzySkin { // Produces a random value between 0 and 1. Thread-safe. static double random_value() { thread_local std::random_device rd; // Hash thread ID for random number seed if no hardware rng seed is available thread_local std::mt19937 gen(rd.entropy() > 0 ? rd() : std::hash()(std::this_thread::get_id())); thread_local std::uniform_real_distribution dist(0.0, 1.0); return dist(gen); } void fuzzy_polyline(Points &poly, const bool closed, const double fuzzy_skin_thickness, const double fuzzy_skin_point_distance) { const double min_dist_between_points = fuzzy_skin_point_distance * 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_distance / 2.; double dist_left_over = random_value() * (min_dist_between_points / 2.); // the distance to be traversed on the line before making the first new point Points out; out.reserve(poly.size()); // Skip the first point for open polyline. Point *p0 = closed ? &poly.back() : &poly.front(); for (auto it_pt1 = closed ? poly.begin() : std::next(poly.begin()); it_pt1 != poly.end(); ++it_pt1) { Point &p1 = *it_pt1; // 'a' is the (next) new point between p0 and p1 Vec2d p0p1 = (p1 - *p0).cast(); double p0p1_size = p0p1.norm(); double p0pa_dist = dist_left_over; for (; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + random_value() * range_random_point_dist) { double r = random_value() * (fuzzy_skin_thickness * 2.) - fuzzy_skin_thickness; out.emplace_back(*p0 + (p0p1 * (p0pa_dist / p0p1_size) + perp(p0p1).cast().normalized() * r).cast()); } dist_left_over = p0pa_dist - p0p1_size; 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 = std::move(out); } } void fuzzy_polygon(Polygon &polygon, double fuzzy_skin_thickness, double fuzzy_skin_point_distance) { fuzzy_polyline(polygon.points, true, fuzzy_skin_thickness, fuzzy_skin_point_distance); } void fuzzy_extrusion_line(Arachne::ExtrusionLine &ext_lines, const double fuzzy_skin_thickness, const double fuzzy_skin_point_distance) { const double min_dist_between_points = fuzzy_skin_point_distance * 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_distance / 2.; double dist_left_over = random_value() * (min_dist_between_points / 2.); // the distance to be traversed on the line before making the first new point Arachne::ExtrusionJunction *p0 = &ext_lines.front(); Arachne::ExtrusionJunctions out; out.reserve(ext_lines.size()); for (auto &p1 : ext_lines) { if (p0->p == p1.p) { // Copy the first point. out.emplace_back(p1.p, p1.w, p1.perimeter_index); continue; } // 'a' is the (next) new point between p0 and p1 Vec2d p0p1 = (p1.p - p0->p).cast(); double p0p1_size = p0p1.norm(); double p0pa_dist = dist_left_over; for (; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + random_value() * range_random_point_dist) { double r = random_value() * (fuzzy_skin_thickness * 2.) - fuzzy_skin_thickness; out.emplace_back(p0->p + (p0p1 * (p0pa_dist / p0p1_size) + perp(p0p1).cast().normalized() * r).cast(), p1.w, p1.perimeter_index); } dist_left_over = p0pa_dist - p0p1_size; p0 = &p1; } while (out.size() < 3) { size_t point_idx = ext_lines.size() - 2; out.emplace_back(ext_lines[point_idx].p, ext_lines[point_idx].w, ext_lines[point_idx].perimeter_index); if (point_idx == 0) { break; } --point_idx; } if (ext_lines.back().p == ext_lines.front().p) { // Connect endpoints. out.front().p = out.back().p; } if (out.size() >= 3) { ext_lines.junctions = std::move(out); } } bool should_fuzzify(const PrintRegionConfig &config, const size_t layer_idx, const size_t perimeter_idx, const bool is_contour) { const FuzzySkinType fuzzy_skin_type = config.fuzzy_skin.value; if (fuzzy_skin_type == FuzzySkinType::None || layer_idx <= 0) { return false; } const bool fuzzify_contours = perimeter_idx == 0; const bool fuzzify_holes = fuzzify_contours && fuzzy_skin_type == FuzzySkinType::All; return is_contour ? fuzzify_contours : fuzzify_holes; } Polygon apply_fuzzy_skin(const Polygon &polygon, const PrintRegionConfig &base_config, const PerimeterRegions &perimeter_regions, const size_t layer_idx, const size_t perimeter_idx, const bool is_contour) { using namespace Slic3r::Algorithm::LineSegmentation; auto apply_fuzzy_skin_on_polygon = [&layer_idx, &perimeter_idx, &is_contour](const Polygon &polygon, const PrintRegionConfig &config) -> Polygon { if (should_fuzzify(config, layer_idx, perimeter_idx, is_contour)) { Polygon fuzzified_polygon = polygon; fuzzy_polygon(fuzzified_polygon, scaled(config.fuzzy_skin_thickness.value), scaled(config.fuzzy_skin_point_dist.value)); return fuzzified_polygon; } else { return polygon; } }; if (perimeter_regions.empty()) { return apply_fuzzy_skin_on_polygon(polygon, base_config); } PolylineRegionSegments segments = polygon_segmentation(polygon, base_config, perimeter_regions); if (segments.size() == 1) { const PrintRegionConfig &config = segments.front().config; return apply_fuzzy_skin_on_polygon(polygon, config); } Polygon fuzzified_polygon; for (PolylineRegionSegment &segment : segments) { const PrintRegionConfig &config = segment.config; if (should_fuzzify(config, layer_idx, perimeter_idx, is_contour)) { fuzzy_polyline(segment.polyline.points, false, scaled(config.fuzzy_skin_thickness.value), scaled(config.fuzzy_skin_point_dist.value)); } assert(!segment.polyline.empty()); if (segment.polyline.empty()) { continue; } else if (!fuzzified_polygon.empty() && fuzzified_polygon.back() == segment.polyline.front()) { // Remove the last point to avoid duplicate points. fuzzified_polygon.points.pop_back(); } Slic3r::append(fuzzified_polygon.points, std::move(segment.polyline.points)); } assert(!fuzzified_polygon.empty()); if (fuzzified_polygon.front() == fuzzified_polygon.back()) { // Remove the last point to avoid duplicity between the first and the last point. fuzzified_polygon.points.pop_back(); } return fuzzified_polygon; } Arachne::ExtrusionLine apply_fuzzy_skin(const Arachne::ExtrusionLine &extrusion, const PrintRegionConfig &base_config, const PerimeterRegions &perimeter_regions, const size_t layer_idx, const size_t perimeter_idx, const bool is_contour) { using namespace Slic3r::Algorithm::LineSegmentation; using namespace Slic3r::Arachne; if (perimeter_regions.empty()) { if (should_fuzzify(base_config, layer_idx, perimeter_idx, is_contour)) { ExtrusionLine fuzzified_extrusion = extrusion; fuzzy_extrusion_line(fuzzified_extrusion, scaled(base_config.fuzzy_skin_thickness.value), scaled(base_config.fuzzy_skin_point_dist.value)); return fuzzified_extrusion; } else { return extrusion; } } ExtrusionRegionSegments segments = extrusion_segmentation(extrusion, base_config, perimeter_regions); ExtrusionLine fuzzified_extrusion(extrusion.inset_idx, extrusion.is_odd, extrusion.is_closed); for (ExtrusionRegionSegment &segment : segments) { const PrintRegionConfig &config = segment.config; if (should_fuzzify(config, layer_idx, perimeter_idx, is_contour)) { fuzzy_extrusion_line(segment.extrusion, scaled(config.fuzzy_skin_thickness.value), scaled(config.fuzzy_skin_point_dist.value)); } assert(!segment.extrusion.empty()); if (segment.extrusion.empty()) { continue; } else if (!fuzzified_extrusion.empty() && fuzzified_extrusion.back().p == segment.extrusion.front().p) { // Remove the last point to avoid duplicate points (We don't care if the width of both points is different.). fuzzified_extrusion.junctions.pop_back(); } Slic3r::append(fuzzified_extrusion.junctions, std::move(segment.extrusion.junctions)); } assert(!fuzzified_extrusion.empty()); return fuzzified_extrusion; } } // namespace Slic3r::Feature::FuzzySkin