diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index ff2f3a8d1..3df927266 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -244,6 +244,8 @@ set(lisbslic3r_sources ParameterUtils.hpp PerimeterGenerator.cpp PerimeterGenerator.hpp + OverhangDetector.hpp + OverhangDetector.cpp PlaceholderParser.cpp PlaceholderParser.hpp Platform.cpp diff --git a/src/libslic3r/OverhangDetector.cpp b/src/libslic3r/OverhangDetector.cpp new file mode 100644 index 000000000..b2f8b6d12 --- /dev/null +++ b/src/libslic3r/OverhangDetector.cpp @@ -0,0 +1,306 @@ +#include "OverhangDetector.hpp" +#include "ExtrusionEntity.hpp" +#include "AABBTreeLines.hpp" +#include "Arachne/utils/ExtrusionLine.hpp" + +namespace Slic3r { + + Polylines ZPath_to_polylines(const ZPaths& paths) + { + Polylines lines; + for (auto& path : paths) { + lines.emplace_back(); + for (auto& p : path) lines.back().points.push_back(Point{ p.x(), p.y() }); + } + return lines; + }; + + ZPaths clip_extrusion(const ZPath& subject, const ZPaths& clip, ClipperLib_Z::ClipType clipType) + { + ClipperLib_Z::Clipper clipper; + clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot, const ClipperLib_Z::IntPoint& e2top, + ClipperLib_Z::IntPoint& pt) { + // The clipping contour may be simplified by clipping it with a bounding box of "subject" path. + // The clipping function used may produce self intersections outside of the "subject" bounding box. Such self intersections are + // harmless to the result of the clipping operation, + // Both ends of each edge belong to the same source: Either they are from subject or from clipping path. + assert(e1bot.z() >= 0 && e1top.z() >= 0); + assert(e2bot.z() >= 0 && e2top.z() >= 0); + assert((e1bot.z() == 0) == (e1top.z() == 0)); + assert((e2bot.z() == 0) == (e2top.z() == 0)); + + // Start & end points of the clipped polyline (extrusion path with a non-zero width). + ClipperLib_Z::IntPoint start = e1bot; + ClipperLib_Z::IntPoint end = e1top; + if (start.z() <= 0 && end.z() <= 0) { + start = e2bot; + end = e2top; + } + + if (start.z() <= 0 && end.z() <= 0) { + // Self intersection on the source contour. + assert(start.z() == 0 && end.z() == 0); + pt.z() = 0; + } + else { + // Interpolate extrusion line width. + assert(start.z() > 0 && end.z() > 0); + + double length_sqr = (end - start).cast().squaredNorm(); + double dist_sqr = (pt - start).cast().squaredNorm(); + double t = std::sqrt(dist_sqr / length_sqr); + + pt.z() = start.z() + coord_t((end.z() - start.z()) * t); + } + }); + + clipper.AddPath(subject, ClipperLib_Z::ptSubject, false); + clipper.AddPaths(clip, ClipperLib_Z::ptClip, true); + + ClipperLib_Z::PolyTree clipped_polytree; + ClipperLib_Z::Paths clipped_paths; + clipper.Execute(clipType, clipped_polytree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero); + ClipperLib_Z::PolyTreeToPaths(clipped_polytree, clipped_paths); + + // Clipped path could contain vertices from the clip with a Z coordinate equal to zero. + // For those vertices, we must assign value based on the subject. + // This happens only in sporadic cases. + for (ClipperLib_Z::Path& path : clipped_paths) + for (ClipperLib_Z::IntPoint& c_pt : path) + if (c_pt.z() == 0) { + // Now we must find the corresponding line on with this point is located and compute line width (Z coordinate). + if (subject.size() <= 2) continue; + + const Point pt(c_pt.x(), c_pt.y()); + Point projected_pt_min; + auto it_min = subject.begin(); + auto dist_sqr_min = std::numeric_limits::max(); + Point prev(subject.front().x(), subject.front().y()); + for (auto it = std::next(subject.begin()); it != subject.end(); ++it) { + Point curr(it->x(), it->y()); + Point projected_pt = pt.projection_onto(Line(prev, curr)); + if (double dist_sqr = (projected_pt - pt).cast().squaredNorm(); dist_sqr < dist_sqr_min) { + dist_sqr_min = dist_sqr; + projected_pt_min = projected_pt; + it_min = std::prev(it); + } + prev = curr; + } + + assert(dist_sqr_min <= SCALED_EPSILON); + assert(std::next(it_min) != subject.end()); + + const Point pt_a(it_min->x(), it_min->y()); + const Point pt_b(std::next(it_min)->x(), std::next(it_min)->y()); + const double line_len = (pt_b - pt_a).cast().norm(); + const double dist = (projected_pt_min - pt_a).cast().norm(); + c_pt.z() = coord_t(double(it_min->z()) + (dist / line_len) * double(std::next(it_min)->z() - it_min->z())); + } + + assert([&clipped_paths = std::as_const(clipped_paths)]() -> bool { + for (const ClipperLib_Z::Path& path : clipped_paths) + for (const ClipperLib_Z::IntPoint& pt : path) + if (pt.z() <= 0) return false; + return true; + }()); + + return clipped_paths; + } + + ZPath add_sampling_points(const ZPath& path, double min_sampling_interval) + { + ZPath sampled_path; + if (path.empty()) + return sampled_path; + + for (size_t idx = 0; idx < path.size(); ++idx) { + ZPoint curr_zp = path[idx]; + Point curr_p = { curr_zp.x(), curr_zp.y() }; + sampled_path.emplace_back(curr_zp); + if (idx + 1 < path.size()) { + ZPoint next_zp = path[idx + 1]; + Point next_p = { next_zp.x(), next_zp.y() }; + + double dist = (next_p - curr_p).cast().norm(); + if (dist > min_sampling_interval) { + size_t num_samples = static_cast(std::floor(dist / min_sampling_interval)); + for (size_t j = 1; j <= num_samples; ++j) { + double t = j * min_sampling_interval / dist; + ZPoint new_point; + new_point.x() = curr_p.x() + t * (next_p.x() - curr_p.x()); + new_point.y() = curr_p.y() + t * (next_p.y() - curr_p.y()); + new_point.z() = curr_zp.z() + t * (next_zp.z() - curr_zp.z()); + sampled_path.push_back(new_point); + } + } + } + } + return sampled_path; + } + + OverhangDistancer::OverhangDistancer(const Polygons layer_polygons) + { + for (const Polygon& island : layer_polygons) { + for (const auto& line : island.lines()) { lines.emplace_back(line.a.cast(), line.b.cast()); } + } + tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines); + } + + float OverhangDistancer::distance_from_perimeter(const Vec2f& point) const + { + Vec2d p = point.cast(); + size_t hit_idx_out{}; + Vec2d hit_point_out = Vec2d::Zero(); + auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, hit_idx_out, hit_point_out); + if (distance < 0) { return std::numeric_limits::max(); } + + distance = sqrt(distance); + return distance; + } + + ExtrusionPaths detect_overhang_degree(const Flow& flow, + const ExtrusionRole role, + const Polygons& lower_polys, + const ClipperLib_Z::Paths& clip_paths, + const ClipperLib_Z::Path& extrusion_path, + const double nozzle_diameter) + { + using ZPath = ClipperLib_Z::Path; + using ZPaths = ClipperLib_Z::Paths; + using ZPoint = ClipperLib_Z::IntPoint; + + ExtrusionPaths ret_paths; + + ZPath subject_path = add_sampling_points(extrusion_path, scale_(2)); + + //Polylines polylines = ZPath_to_polylines(subject_paths); + + ZPaths paths_in_range = clip_extrusion(subject_path, clip_paths, ClipperLib_Z::ctIntersection); + //Polylines path_in_range_debug = ZPath_to_polylines(paths_in_range); + + for (auto& path : paths_in_range) { + for (auto& p : path) { assert(p.z() != 0); } + } + + auto in_same_degree_range = [](double a, double b) -> bool { + int ceil_a = std::ceil(a); + int floor_a = std::floor(a); + int ceil_b = std::ceil(b); + int floor_b = std::floor(b); + return ceil_a == ceil_b && floor_a == floor_b; + }; + + struct SplitPoint + { + Point p; + coord_t w; + double degree; + }; + + auto get_split_points = [](const Point& pa, const Point& pb, const coord_t wa, const coord_t wb, const double da, const double db) -> std::vector { + std::vector ret; + + int start_d = (int)(std::ceil(std::min(da, db))); + int end_d = (int)(std::floor(std::max(da, db))); + + if (start_d > end_d) return ret; + + double delta_d = db - da; + if (std::abs(delta_d) < 1e-6) return ret; + + if (da < db) { + for (int k = start_d; k <= end_d; ++k) { + const double t = (k - da) / delta_d; + const Point pt = pa + (pb - pa) * t; + const coord_t w = wa + coord_t((wb - wa) * t); + ret.emplace_back(SplitPoint{ pt, w, (double)k }); + } + } + else { + for (int k = end_d; k >= start_d; --k) { + const double t = (k - da) / delta_d; + const Point pt = pa + (pb - pa) * t; + const coord_t w = wa + coord_t((wb - wa) * t); + ret.emplace_back(SplitPoint{ pt, w, (double)k }); + } + } + + return ret; + }; + + std::unique_ptr prev_layer_distancer = std::make_unique(lower_polys); + + coord_t offset_width = scale_(nozzle_diameter) / 2; + + for (auto& path : paths_in_range) { + std::vector overhang_degree_arr; + if (path.empty()) continue; + for (size_t idx = 0; idx < path.size(); ++idx) { + Point p{ path[idx].x(), path[idx].y() }; + float overhang_dist = prev_layer_distancer->distance_from_perimeter(p.cast()); + float width = path[idx].z(); + double real_dist = offset_width - overhang_dist; + + double degree = 0; + + if (std::abs(real_dist) > (width / 2)) { + degree = real_dist < 0 ? 0 : 100; + } + else { + degree = (width / 2 + real_dist) / width * 100; + } + + double mapped_degree; + // map overhang speed to a range + { + auto it = std::upper_bound(non_uniform_degree_map.begin(), non_uniform_degree_map.end(), degree); + int high_idx = it - non_uniform_degree_map.begin(); + int low_idx = high_idx - 1; + double t = (degree - non_uniform_degree_map[low_idx]) / (non_uniform_degree_map[high_idx] - non_uniform_degree_map[low_idx]); + mapped_degree = low_idx * (1 - t) + t * high_idx; + } + overhang_degree_arr.emplace_back(mapped_degree); + } + // split into extrusion path + + Point prev_p{ path.front().x(), path.front().y() }; + coord_t prev_w = path.front().z(); + double prev_d = overhang_degree_arr.front(); + ZPath prev_line = { path.front() }; + + for (size_t idx = 1; idx < path.size(); ++idx) { + Point curr_p{ path[idx].x(), path[idx].y() }; + coord_t curr_w = path[idx].z(); + double curr_d = overhang_degree_arr[idx]; + + if (in_same_degree_range(prev_d, curr_d)) { + prev_w = curr_w; + prev_d = curr_d; + prev_p = curr_p; + prev_line.push_back(path[idx]); + continue; + } + else { + auto split_points = get_split_points(prev_p, curr_p, prev_w, curr_w, prev_d, curr_d); + for (auto& split_point : split_points) { + prev_line.push_back({ split_point.p.x(), split_point.p.y(), split_point.w }); + double target_degree = 0; + if (prev_d < curr_d) + target_degree = split_point.degree - 1; + else + target_degree = split_point.degree; + extrusion_paths_append(ret_paths, { prev_line }, role, flow, target_degree); + prev_line.clear(); + prev_line.push_back({ split_point.p.x(), split_point.p.y(), split_point.w }); + } + prev_w = curr_w; + prev_d = curr_d; + prev_p = curr_p; + prev_line.push_back(path[idx]); + } + } + if (prev_line.size() > 1) { extrusion_paths_append(ret_paths, { prev_line }, role, flow, std::floor(prev_d)); } + } + return ret_paths; + } +} \ No newline at end of file diff --git a/src/libslic3r/OverhangDetector.hpp b/src/libslic3r/OverhangDetector.hpp new file mode 100644 index 000000000..90e63a6b8 --- /dev/null +++ b/src/libslic3r/OverhangDetector.hpp @@ -0,0 +1,40 @@ +#ifndef OVERHANG_DETECTOR_HPP +#define OVERHANG_DETECTOR_HPP + +#include "AABBTreeIndirect.hpp" +#include "ExtrusionEntityCollection.hpp" +#include "ClipperUtils.hpp" +#include "Flow.hpp" + +using ZPoint = ClipperLib_Z::IntPoint; +using ZPath = ClipperLib_Z::Path; +using ZPaths = ClipperLib_Z::Paths; + +static const int overhang_sampling_number = 6; +static const double min_degree_gap = 0.1; +static const int max_overhang_degree = overhang_sampling_number - 1; +static const std::vector non_uniform_degree_map = { 0, 10, 25, 50, 75, 100}; +static const int insert_point_count = 3; + +namespace Slic3r { + + class OverhangDistancer + { + std::vector lines; + AABBTreeIndirect::Tree<2, double> tree; + + public: + OverhangDistancer(const Polygons layer_polygons); + float distance_from_perimeter(const Vec2f& point) const; + }; + + + ZPaths clip_extrusion(const ZPath& subject, const ZPaths& clip, ClipperLib_Z::ClipType clipType); + + ZPath add_sampling_points(const ZPath& path, double min_sampling_interval); + + ExtrusionPaths detect_overhang_degree(const Flow& flow, const ExtrusionRole role, const Polygons& lower_polys, const ClipperLib_Z::Paths& clip_paths, const ClipperLib_Z::Path& extrusion_paths, const double nozzle_diameter); + +} + +#endif \ No newline at end of file diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index b3169848a..e8b549bf2 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -13,13 +13,9 @@ #include #include #include -#include "libslic3r/AABBTreeLines.hpp" -static const int overhang_sampling_number = 6; +#include "OverhangDetector.hpp" + static const double narrow_loop_length_threshold = 10; -static const double min_degree_gap = 0.1; -static const int max_overhang_degree = overhang_sampling_number - 1; -static const std::vector non_uniform_degree_map = { 0, 10, 25, 50, 75, 100}; -static const int insert_point_count = 3; //BBS: when the width of expolygon is smaller than //ext_perimeter_width + ext_perimeter_spacing * (1 - SMALLER_EXT_INSET_OVERLAP_TOLERANCE), //we think it's small detail area and will generate smaller line width for it @@ -311,35 +307,6 @@ static void insert_point_to_line( double left_point_degree, } } -class OverhangDistancer -{ - std::vector lines; - AABBTreeIndirect::Tree<2, double> tree; - -public: - OverhangDistancer(const Polygons layer_polygons) - { - for (const Polygon &island : layer_polygons) { - for (const auto &line : island.lines()) { - lines.emplace_back(line.a.cast(), line.b.cast()); - } - } - tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines); - } - - float distance_from_perimeter(const Vec2f &point) const - { - Vec2d p = point.cast(); - size_t hit_idx_out{}; - Vec2d hit_point_out = Vec2d::Zero(); - auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, hit_idx_out, hit_point_out); - if (distance < 0) { return std::numeric_limits::max(); } - - distance = sqrt(distance); - return distance; - } -}; - static void sampling_at_line_end(Polyline &poly, double mini_length, int insert_count) { @@ -371,6 +338,7 @@ static void sampling_at_line_end(Polyline &poly, double mini_length, int insert_ poly.append(end_point); } + static std::deque detect_overahng_degree(Polygons lower_polygons, Polylines middle_overhang_polyines, const double &lower_bound, @@ -728,99 +696,6 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime return out; } -static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path& subject, const ClipperLib_Z::Paths& clip, ClipperLib_Z::ClipType clipType) -{ - ClipperLib_Z::Clipper clipper; - clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot, - const ClipperLib_Z::IntPoint& e2top, ClipperLib_Z::IntPoint& pt) { - // The clipping contour may be simplified by clipping it with a bounding box of "subject" path. - // The clipping function used may produce self intersections outside of the "subject" bounding box. Such self intersections are - // harmless to the result of the clipping operation, - // Both ends of each edge belong to the same source: Either they are from subject or from clipping path. - assert(e1bot.z() >= 0 && e1top.z() >= 0); - assert(e2bot.z() >= 0 && e2top.z() >= 0); - assert((e1bot.z() == 0) == (e1top.z() == 0)); - assert((e2bot.z() == 0) == (e2top.z() == 0)); - - // Start & end points of the clipped polyline (extrusion path with a non-zero width). - ClipperLib_Z::IntPoint start = e1bot; - ClipperLib_Z::IntPoint end = e1top; - if (start.z() <= 0 && end.z() <= 0) { - start = e2bot; - end = e2top; - } - - if (start.z() <= 0 && end.z() <= 0) { - // Self intersection on the source contour. - assert(start.z() == 0 && end.z() == 0); - pt.z() = 0; - } - else { - // Interpolate extrusion line width. - assert(start.z() > 0 && end.z() > 0); - - double length_sqr = (end - start).cast().squaredNorm(); - double dist_sqr = (pt - start).cast().squaredNorm(); - double t = std::sqrt(dist_sqr / length_sqr); - - pt.z() = start.z() + coord_t((end.z() - start.z()) * t); - } - }); - - clipper.AddPath(subject, ClipperLib_Z::ptSubject, false); - clipper.AddPaths(clip, ClipperLib_Z::ptClip, true); - - ClipperLib_Z::PolyTree clipped_polytree; - ClipperLib_Z::Paths clipped_paths; - clipper.Execute(clipType, clipped_polytree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero); - ClipperLib_Z::PolyTreeToPaths(clipped_polytree, clipped_paths); - - // Clipped path could contain vertices from the clip with a Z coordinate equal to zero. - // For those vertices, we must assign value based on the subject. - // This happens only in sporadic cases. - for (ClipperLib_Z::Path& path : clipped_paths) - for (ClipperLib_Z::IntPoint& c_pt : path) - if (c_pt.z() == 0) { - // Now we must find the corresponding line on with this point is located and compute line width (Z coordinate). - if (subject.size() <= 2) - continue; - - const Point pt(c_pt.x(), c_pt.y()); - Point projected_pt_min; - auto it_min = subject.begin(); - auto dist_sqr_min = std::numeric_limits::max(); - Point prev(subject.front().x(), subject.front().y()); - for (auto it = std::next(subject.begin()); it != subject.end(); ++it) { - Point curr(it->x(), it->y()); - Point projected_pt = pt.projection_onto(Line(prev, curr)); - if (double dist_sqr = (projected_pt - pt).cast().squaredNorm(); dist_sqr < dist_sqr_min) { - dist_sqr_min = dist_sqr; - projected_pt_min = projected_pt; - it_min = std::prev(it); - } - prev = curr; - } - - assert(dist_sqr_min <= SCALED_EPSILON); - assert(std::next(it_min) != subject.end()); - - const Point pt_a(it_min->x(), it_min->y()); - const Point pt_b(std::next(it_min)->x(), std::next(it_min)->y()); - const double line_len = (pt_b - pt_a).cast().norm(); - const double dist = (projected_pt_min - pt_a).cast().norm(); - c_pt.z() = coord_t(double(it_min->z()) + (dist / line_len) * double(std::next(it_min)->z() - it_min->z())); - } - - assert([&clipped_paths = std::as_const(clipped_paths)]() -> bool { - for (const ClipperLib_Z::Path& path : clipped_paths) - for (const ClipperLib_Z::IntPoint& pt : path) - if (pt.z() <= 0) - return false; - return true; - }()); - - return clipped_paths; -} struct PerimeterGeneratorArachneExtrusion { @@ -941,6 +816,9 @@ static void detect_brigde_wall_arachne(const PerimeterGenerator &perimeter_gener static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& perimeter_generator, std::vector& pg_extrusions) { + using ZPath = ClipperLib_Z::Path; + using ZPaths = ClipperLib_Z::Paths; + ExtrusionEntityCollection extrusion_coll; if (perimeter_generator.print_config->z_direction_outwall_speed_continuous) extrusion_coll.loop_node_range.first = perimeter_generator.loop_nodes->size(); @@ -975,94 +853,61 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p ExtrusionPaths paths; // detect overhanging/bridging perimeters if (perimeter_generator.config->detect_overhang_wall && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers) { - ClipperLib_Z::Path extrusion_path; + ClipperLib_Z::Path extrusion_path; extrusion_path.reserve(extrusion->size()); + + double nozzle_diameter = perimeter_generator.print_config->nozzle_diameter.get_at(perimeter_generator.config->wall_filament - 1); + Polygons lower_layer_polys = perimeter_generator.lower_slices_polygons(); + + coord_t max_extrusion_width = 0; BoundingBox extrusion_path_bbox; for (const Arachne::ExtrusionJunction &ej : extrusion->junctions) { extrusion_path.emplace_back(ej.p.x(), ej.p.y(), ej.w); extrusion_path_bbox.merge(Point(ej.p.x(), ej.p.y())); + max_extrusion_width = std::max(max_extrusion_width, ej.w); + } + extrusion_path_bbox.inflated(max_extrusion_width+scale_(nozzle_diameter)); + + Polygons new_lower_polys; + for (size_t idx = 0; idx < lower_layer_polys.size(); ++idx) { + auto new_poly = ClipperUtils::clip_clipper_polygon_with_subject_bbox(lower_layer_polys[idx], extrusion_path_bbox,true); + if (!new_poly.empty()) + new_lower_polys.emplace_back(new_poly); } - ClipperLib_Z::Paths lower_slices_paths; - { - lower_slices_paths.reserve(perimeter_generator.lower_slices_polygons().size()); - Points clipped; - extrusion_path_bbox.offset(SCALED_EPSILON); - for (const Polygon &poly : perimeter_generator.lower_slices_polygons()) { - clipped.clear(); - ClipperUtils::clip_clipper_polygon_with_subject_bbox(poly.points, extrusion_path_bbox, clipped); - if (!clipped.empty()) { - lower_slices_paths.emplace_back(); - ClipperLib_Z::Path &out = lower_slices_paths.back(); - out.reserve(clipped.size()); - for (const Point &pt : clipped) - out.emplace_back(pt.x(), pt.y(), 0); - } - } - } + lower_layer_polys = new_lower_polys; - ExtrusionPaths temp_paths; - // get non-overhang paths by intersecting this loop with the grown lower slices - extrusion_paths_append(temp_paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role, - is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow); + ZPath subject_path; + for (auto& ej : extrusion->junctions) + subject_path.emplace_back(ej.p.x(), ej.p.y(), ej.w); + + ZPaths clip_paths; + for (auto& poly : lower_layer_polys) { + clip_paths.emplace_back(); + for (auto& p : poly) + clip_paths.back().emplace_back(p.x(), p.y(), 0); + } if (perimeter_generator.config->enable_overhang_speed.get_at(get_extruder_index(*(perimeter_generator.print_config), perimeter_generator.config->wall_filament - 1)) && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) { - + bool is_external = extrusion->inset_idx == 0; Flow flow = is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow; - std::map> clipper_serise; - - std::map recognization_paths; - for (const ExtrusionPath &path : temp_paths) { - if (recognization_paths.count(path.width)) - recognization_paths[path.width].emplace_back(std::move(path)); - else - recognization_paths.insert(std::pair(path.width, {std::move(path)})); - } - - for (const auto &it : recognization_paths) { - Polylines be_clipped; - - for (const ExtrusionPath &p : it.second) { - be_clipped.emplace_back(std::move(p.polyline)); - } - - BoundingBox extrusion_bboxs = get_extents(be_clipped); - //ExPolygons lower_slcier_chopped = *perimeter_generator.lower_slices; - Polygons lower_slcier_chopped=ClipperUtils::clip_clipper_polygons_with_subject_bbox(*perimeter_generator.lower_slices, extrusion_bboxs, true); - - double start_pos = -it.first * 0.5; - double end_pos = 0.5 * it.first; - - Polylines remain_polylines; - std::vector degree_polygons; - for (int j = 0; j < overhang_sampling_number; j++) { - Polygons limiton_polygons = offset(lower_slcier_chopped, float(scale_(start_pos + (j + 0.5) * (end_pos - start_pos) / (overhang_sampling_number - 1)))); - - Polylines inside_polines = j == 0 ? intersection_pl_2(be_clipped, limiton_polygons) : intersection_pl_2(remain_polylines, limiton_polygons); - - remain_polylines = j == 0 ? diff_pl_2(be_clipped, limiton_polygons) : diff_pl_2(remain_polylines, limiton_polygons); - - extrusion_paths_append(paths, std::move(inside_polines), j, int(0), role, it.second.front().mm3_per_mm, it.second.front().width, it.second.front().height); - - if (remain_polylines.size() == 0) break; - } - - if (remain_polylines.size() != 0) { - extrusion_paths_append(paths, std::move(remain_polylines), overhang_sampling_number - 1, int(0), erOverhangPerimeter, it.second.front().mm3_per_mm, it.second.front().width, it.second.front().height); - } - } - - } else { - paths = std::move(temp_paths); - + ExtrusionRole role = is_external ? ExtrusionRole::erExternalPerimeter : ExtrusionRole::erPerimeter; + paths = detect_overhang_degree(flow, role, lower_layer_polys, clip_paths, subject_path, nozzle_diameter); + } + else { + ExtrusionPaths temp_paths; + ZPaths path_non_overhang = clip_extrusion(subject_path, clip_paths, ClipperLib_Z::ctIntersection); + // get non-overhang paths by intersecting this loop with the grown lower slices + extrusion_paths_append(temp_paths, path_non_overhang, role, + is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow); + paths = std::move(temp_paths); } // get overhang paths by checking what parts of this loop fall // outside the grown lower slices (thus where the distance between // the loop centerline and original lower slices is >= half nozzle diameter // detect if the overhang perimeter is bridge - ClipperLib_Z::Paths path_overhang = clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctDifference); - + ZPaths path_overhang = clip_extrusion(subject_path, clip_paths, ClipperLib_Z::ctDifference); bool zero_z_support = (perimeter_generator.object_config->enable_support || perimeter_generator.object_config->enforce_support_layers > 0) && perimeter_generator.object_config->support_top_z_distance.value == 0; if(zero_z_support) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index ed36ad2b5..47b18e338 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -423,6 +423,7 @@ void PrintObject::make_perimeters() #endif BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start"; +#if 1 tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), [this](const tbb::blocked_range& range) { @@ -432,6 +433,12 @@ void PrintObject::make_perimeters() } } ); +#else + for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) { + m_print->throw_if_canceled(); + m_layers[layer_idx]->make_perimeters(); + } +#endif m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end";