diff --git a/src/libslic3r/AABBTreeIndirect.hpp b/src/libslic3r/AABBTreeIndirect.hpp index 9b9c886ec..6d6479508 100644 --- a/src/libslic3r/AABBTreeIndirect.hpp +++ b/src/libslic3r/AABBTreeIndirect.hpp @@ -13,6 +13,7 @@ #include +#include "BoundingBox.hpp" #include "Utils.hpp" // for next_highest_power_of_2() // Definition of the ray intersection hit structure. @@ -83,6 +84,13 @@ public: // to split around. template void build(std::vector &&input) + { + this->build_modify_input(input); + input.clear(); + } + + template + void build_modify_input(std::vector &input) { if (input.empty()) clear(); @@ -91,7 +99,6 @@ public: m_nodes.assign(next_highest_power_of_2(input.size()) * 2 - 1, Node()); build_recursive(input, 0, 0, input.size() - 1); } - input.clear(); } const std::vector& nodes() const { return m_nodes; } @@ -211,6 +218,23 @@ using Tree3f = Tree<3, float>; using Tree2d = Tree<2, double>; using Tree3d = Tree<3, double>; +// Wrap a 2D Slic3r own BoundingBox to be passed to Tree::build() and similar +// to build an AABBTree over coord_t 2D bounding boxes. +class BoundingBoxWrapper { +public: + using BoundingBox = Eigen::AlignedBox; + BoundingBoxWrapper(const size_t idx, const Slic3r::BoundingBox &bbox) : + m_idx(idx), + // Inflate the bounding box a bit to account for numerical issues. + m_bbox(bbox.min - Point(SCALED_EPSILON, SCALED_EPSILON), bbox.max + Point(SCALED_EPSILON, SCALED_EPSILON)) {} + size_t idx() const { return m_idx; } + const BoundingBox& bbox() const { return m_bbox; } + Point centroid() const { return ((m_bbox.min().cast() + m_bbox.max().cast()) / 2).cast(); } +private: + size_t m_idx; + BoundingBox m_bbox; +}; + namespace detail { template struct RayIntersector { @@ -513,7 +537,7 @@ namespace detail { const VectorType origin; inline VectorType closest_point_to_origin(size_t primitive_index, - ScalarType& squared_distance){ + ScalarType& squared_distance) const { const auto &triangle = this->faces[primitive_index]; VectorType closest_point = closest_point_to_triangle(origin, this->vertices[triangle(0)].template cast(), @@ -607,6 +631,37 @@ namespace detail { return up_sqr_d; } + template + static inline void indexed_primitives_within_distance_squared_recurisve(const IndexedPrimitivesDistancerType &distancer, + size_t node_idx, + Scalar squared_distance_limit, + std::vector &found_primitives_indices) + { + const auto &node = distancer.tree.node(node_idx); + assert(node.is_valid()); + if (node.is_leaf()) { + Scalar sqr_dist; + distancer.closest_point_to_origin(node.idx, sqr_dist); + if (sqr_dist < squared_distance_limit) { found_primitives_indices.push_back(node.idx); } + } else { + size_t left_node_idx = node_idx * 2 + 1; + size_t right_node_idx = left_node_idx + 1; + const auto &node_left = distancer.tree.node(left_node_idx); + const auto &node_right = distancer.tree.node(right_node_idx); + assert(node_left.is_valid()); + assert(node_right.is_valid()); + + if (node_left.bbox.squaredExteriorDistance(distancer.origin) < squared_distance_limit) { + indexed_primitives_within_distance_squared_recurisve(distancer, left_node_idx, squared_distance_limit, + found_primitives_indices); + } + if (node_right.bbox.squaredExteriorDistance(distancer.origin) < squared_distance_limit) { + indexed_primitives_within_distance_squared_recurisve(distancer, right_node_idx, squared_distance_limit, + found_primitives_indices); + } + } + } + } // namespace detail // Build a balanced AABB Tree over an indexed triangles set, balancing the tree @@ -793,6 +848,33 @@ inline bool is_any_triangle_in_radius( return hit_point.allFinite(); } +// Returns all triangles within the given radius limit +template +inline std::vector all_triangles_in_radius( + // Indexed triangle set - 3D vertices. + const std::vector &vertices, + // Indexed triangle set - triangular faces, references to vertices. + const std::vector &faces, + // AABBTreeIndirect::Tree over vertices & faces, bounding boxes built with the accuracy of vertices. + const TreeType &tree, + // Point to which the distances on the indexed triangle set is searched for. + const VectorType &point, + //Square of maximum distance in which triangles are searched for + typename VectorType::Scalar max_distance_squared) +{ + auto distancer = detail::IndexedTriangleSetDistancer + { vertices, faces, tree, point }; + + if(tree.empty()) + { + return {}; + } + + std::vector found_triangles{}; + detail::indexed_primitives_within_distance_squared_recurisve(distancer, size_t(0), max_distance_squared, found_triangles); + return found_triangles; +} + // Traverse the tree and return the index of an entity whose bounding box // contains a given point. Returns size_t(-1) when the point is outside. @@ -837,48 +919,54 @@ struct Intersecting> { template auto intersecting(const G &g) { return Intersecting{g}; } -template struct Containing {}; +template struct Within {}; // Intersection predicate specialization for box-box intersections template -struct Containing> { +struct Within> { Eigen::AlignedBox box; - Containing(const Eigen::AlignedBox &bb): box{bb} {} + Within(const Eigen::AlignedBox &bb): box{bb} {} bool operator() (const typename Tree::Node &node) const { - return box.contains(node.bbox); + return node.is_leaf() ? box.contains(node.bbox) : box.intersects(node.bbox); } }; -template auto containing(const G &g) { return Containing{g}; } +template auto within(const G &g) { return Within{g}; } namespace detail { +// Returns true in case traversal should continue, +// returns false if traversal should stop (for example if the first hit was found). template -void traverse_recurse(const Tree &tree, +bool traverse_recurse(const Tree &tree, size_t idx, Pred && pred, Fn && callback) { assert(tree.node(idx).is_valid()); - if (!pred(tree.node(idx))) return; + if (!pred(tree.node(idx))) + // Continue traversal. + return true; if (tree.node(idx).is_leaf()) { - callback(tree.node(idx).idx); + // Callback returns true to continue traversal, false to stop traversal. + return callback(tree.node(idx)); } else { // call this with left and right node idx: - auto trv = [&](size_t idx) { - traverse_recurse(tree, idx, std::forward(pred), - std::forward(callback)); + auto trv = [&](size_t idx) -> bool { + return traverse_recurse(tree, idx, std::forward(pred), + std::forward(callback)); }; // Left / right child node index. - trv(Tree::left_child_idx(idx)); - trv(Tree::right_child_idx(idx)); + // Returns true if both children allow the traversal to continue. + return trv(Tree::left_child_idx(idx)) && + trv(Tree::right_child_idx(idx)); } } @@ -888,6 +976,7 @@ void traverse_recurse(const Tree &tree, // traverse(tree, intersecting(QueryBox), [](size_t face_idx) { // /* ... */ // }); +// Callback shall return true to continue traversal, false if it wants to stop traversal, for example if it found the answer. template void traverse(const Tree &tree, Predicate &&pred, Fn &&callback) { diff --git a/src/libslic3r/AABBTreeLines.hpp b/src/libslic3r/AABBTreeLines.hpp index 7b9595419..21678bfcd 100644 --- a/src/libslic3r/AABBTreeLines.hpp +++ b/src/libslic3r/AABBTreeLines.hpp @@ -1,83 +1,197 @@ #ifndef SRC_LIBSLIC3R_AABBTREELINES_HPP_ #define SRC_LIBSLIC3R_AABBTREELINES_HPP_ -#include "libslic3r/Point.hpp" -#include "libslic3r/EdgeGrid.hpp" +#include "Point.hpp" +#include "Utils.hpp" +#include "libslic3r.h" #include "libslic3r/AABBTreeIndirect.hpp" #include "libslic3r/Line.hpp" +#include +#include +#include +#include -namespace Slic3r { - -namespace AABBTreeLines { +namespace Slic3r { namespace AABBTreeLines { namespace detail { -template -struct IndexedLinesDistancer { - using LineType = ALineType; - using TreeType = ATreeType; +template struct IndexedLinesDistancer +{ + using LineType = ALineType; + using TreeType = ATreeType; using VectorType = AVectorType; using ScalarType = typename VectorType::Scalar; const std::vector &lines; - const TreeType &tree; + const TreeType &tree; const VectorType origin; - inline VectorType closest_point_to_origin(size_t primitive_index, - ScalarType &squared_distance) { - VectorType nearest_point; - const LineType &line = lines[primitive_index]; - squared_distance = line_alg::distance_to_squared(line, origin, &nearest_point); - return nearest_point; + inline VectorType closest_point_to_origin(size_t primitive_index, ScalarType &squared_distance) const + { + Vec nearest_point; + const LineType &line = lines[primitive_index]; + squared_distance = line_alg::distance_to_squared(line, origin.template cast(), &nearest_point); + return nearest_point.template cast(); } }; +// returns number of intersections of ray starting in ray_origin and following the specified coordinate line with lines in tree +// first number is hits in positive direction of ray, second number hits in negative direction. returns neagtive numbers when ray_origin is +// on some line exactly. +template +inline std::tuple coordinate_aligned_ray_hit_count(size_t node_idx, + const TreeType &tree, + const std::vector &lines, + const VectorType &ray_origin) +{ + static constexpr int other_coordinate = (coordinate + 1) % 2; + using Scalar = typename LineType::Scalar; + using Floating = typename std::conditional::value, Scalar, double>::type; + const auto &node = tree.node(node_idx); + assert(node.is_valid()); + if (node.is_leaf()) { + const LineType &line = lines[node.idx]; + if (ray_origin[other_coordinate] < std::min(line.a[other_coordinate], line.b[other_coordinate]) || + ray_origin[other_coordinate] >= std::max(line.a[other_coordinate], line.b[other_coordinate])) { + // the second inequality is nonsharp for a reason + // without it, we may count contour border twice when the lines meet exactly at the spot of intersection. this prevents is + return {0, 0}; + } + + Scalar line_max = std::max(line.a[coordinate], line.b[coordinate]); + Scalar line_min = std::min(line.a[coordinate], line.b[coordinate]); + if (ray_origin[coordinate] > line_max) { + return {1, 0}; + } else if (ray_origin[coordinate] < line_min) { + return {0, 1}; + } else { + // find intersection of ray with line + // that is when ( line.a + t * (line.b - line.a) )[other_coordinate] == ray_origin[other_coordinate] + // t = ray_origin[oc] - line.a[oc] / (line.b[oc] - line.a[oc]); + // then we want to get value of intersection[ coordinate] + // val_c = line.a[c] + t * (line.b[c] - line.a[c]); + // Note that ray and line may overlap, when (line.b[oc] - line.a[oc]) is zero + // In that case, we return negative number + Floating distance_oc = line.b[other_coordinate] - line.a[other_coordinate]; + Floating t = (ray_origin[other_coordinate] - line.a[other_coordinate]) / distance_oc; + Floating val_c = line.a[coordinate] + t * (line.b[coordinate] - line.a[coordinate]); + if (ray_origin[coordinate] > val_c) { + return {1, 0}; + } else if (ray_origin[coordinate] < val_c) { + return {0, 1}; + } else { // ray origin is on boundary + return {-1, -1}; + } + } + } else { + int intersections_above = 0; + int intersections_below = 0; + size_t left_node_idx = node_idx * 2 + 1; + size_t right_node_idx = left_node_idx + 1; + const auto &node_left = tree.node(left_node_idx); + const auto &node_right = tree.node(right_node_idx); + assert(node_left.is_valid()); + assert(node_right.is_valid()); + + if (node_left.bbox.min()[other_coordinate] <= ray_origin[other_coordinate] && + node_left.bbox.max()[other_coordinate] >= + ray_origin[other_coordinate]) { + auto [above, below] = coordinate_aligned_ray_hit_count(left_node_idx, tree, lines, + ray_origin); + if (above < 0 || below < 0) return {-1, -1}; + intersections_above += above; + intersections_below += below; + } + + if (node_right.bbox.min()[other_coordinate] <= ray_origin[other_coordinate] && + node_right.bbox.max()[other_coordinate] >= ray_origin[other_coordinate]) { + auto [above, below] = coordinate_aligned_ray_hit_count(right_node_idx, tree, lines, + ray_origin); + if (above < 0 || below < 0) return {-1, -1}; + intersections_above += above; + intersections_below += below; + } + return {intersections_above, intersections_below}; + } } -// Build a balanced AABB Tree over a vector of float lines, balancing the tree +template +inline std::vector> get_intersections_with_line(size_t node_idx, + const TreeType &tree, + const std::vector &lines, + const LineType &line, + const typename TreeType::BoundingBox &line_bb) +{ + const auto &node = tree.node(node_idx); + assert(node.is_valid()); + if (node.is_leaf()) { + VectorType intersection_pt; + if (line_alg::intersection(line, lines[node.idx], &intersection_pt)) { + return {std::pair(intersection_pt, node.idx)}; + } else { + return {}; + } + } else { + size_t left_node_idx = node_idx * 2 + 1; + size_t right_node_idx = left_node_idx + 1; + const auto &node_left = tree.node(left_node_idx); + const auto &node_right = tree.node(right_node_idx); + assert(node_left.is_valid()); + assert(node_right.is_valid()); + + std::vector> result; + + if (node_left.bbox.intersects(line_bb)) { + std::vector> intersections = + get_intersections_with_line(left_node_idx, tree, lines, line, line_bb); + result.insert(result.end(), intersections.begin(), intersections.end()); + } + + if (node_right.bbox.intersects(line_bb)) { + std::vector> intersections = + get_intersections_with_line(right_node_idx, tree, lines, line, line_bb); + result.insert(result.end(), intersections.begin(), intersections.end()); + } + + return result; + } +} + +} // namespace detail + +// Build a balanced AABB Tree over a vector of lines, balancing the tree // on centroids of the lines. // Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies // during tree traversal. template -inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines( - const std::vector &lines, - //FIXME do we want to apply an epsilon? - const float eps = 0) - { +inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(const std::vector &lines) +{ using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>; -// using CoordType = typename TreeType::CoordType; - using VectorType = typename TreeType::VectorType; + // using CoordType = typename TreeType::CoordType; + using VectorType = typename TreeType::VectorType; using BoundingBox = typename TreeType::BoundingBox; - struct InputType { - size_t idx() const { - return m_idx; - } - const BoundingBox& bbox() const { - return m_bbox; - } - const VectorType& centroid() const { - return m_centroid; - } + struct InputType + { + size_t idx() const { return m_idx; } + const BoundingBox &bbox() const { return m_bbox; } + const VectorType ¢roid() const { return m_centroid; } - size_t m_idx; + size_t m_idx; BoundingBox m_bbox; - VectorType m_centroid; + VectorType m_centroid; }; std::vector input; input.reserve(lines.size()); - const VectorType veps(eps, eps); for (size_t i = 0; i < lines.size(); ++i) { const LineType &line = lines[i]; - InputType n; - n.m_idx = i; + InputType n; + n.m_idx = i; n.m_centroid = (line.a + line.b) * 0.5; - n.m_bbox = BoundingBox(line.a, line.a); + n.m_bbox = BoundingBox(line.a, line.a); n.m_bbox.extend(line.b); - n.m_bbox.min() -= veps; - n.m_bbox.max() += veps; input.emplace_back(n); } @@ -88,25 +202,164 @@ inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over // Finding a closest line, its closest point and squared distance to the closest point // Returns squared distance to the closest point or -1 if the input is empty. +// or no closer point than max_sq_dist template inline typename VectorType::Scalar squared_distance_to_indexed_lines( - const std::vector &lines, - const TreeType &tree, - const VectorType &point, - size_t &hit_idx_out, - Eigen::PlainObjectBase &hit_point_out) - { + const std::vector &lines, + const TreeType &tree, + const VectorType &point, + size_t &hit_idx_out, + Eigen::PlainObjectBase &hit_point_out, + typename VectorType::Scalar max_sqr_dist = std::numeric_limits::infinity()) +{ using Scalar = typename VectorType::Scalar; - auto distancer = detail::IndexedLinesDistancer - { lines, tree, point }; - return tree.empty() ? - Scalar(-1) : - AABBTreeIndirect::detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0), - std::numeric_limits::infinity(), hit_idx_out, hit_point_out); + if (tree.empty()) return Scalar(-1); + auto distancer = detail::IndexedLinesDistancer{lines, tree, point}; + return AABBTreeIndirect::detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0), max_sqr_dist, + hit_idx_out, hit_point_out); } +// Returns all lines within the given radius limit +template +inline std::vector all_lines_in_radius(const std::vector &lines, + const TreeType &tree, + const VectorType &point, + typename VectorType::Scalar max_distance_squared) +{ + auto distancer = detail::IndexedLinesDistancer{lines, tree, point}; + + if (tree.empty()) { return {}; } + + std::vector found_lines{}; + AABBTreeIndirect::detail::indexed_primitives_within_distance_squared_recurisve(distancer, size_t(0), max_distance_squared, found_lines); + return found_lines; } +// return 1 if true, -1 if false, 0 for point on contour (or if cannot be determined) +template +inline int point_outside_closed_contours(const std::vector &lines, const TreeType &tree, const VectorType &point) +{ + if (tree.empty()) { return 1; } + + auto [hits_above, hits_below] = detail::coordinate_aligned_ray_hit_count(0, tree, lines, point); + if (hits_above < 0 || hits_below < 0) { + return 0; + } else if (hits_above % 2 == 1 && hits_below % 2 == 1) { + return -1; + } else if (hits_above % 2 == 0 && hits_below % 2 == 0) { + return 1; + } else { // this should not happen with closed contours. lets check it in Y direction + auto [hits_above, hits_below] = detail::coordinate_aligned_ray_hit_count(0, tree, lines, point); + if (hits_above < 0 || hits_below < 0) { + return 0; + } else if (hits_above % 2 == 1 && hits_below % 2 == 1) { + return -1; + } else if (hits_above % 2 == 0 && hits_below % 2 == 0) { + return 1; + } else { // both results were unclear + return 0; + } + } } +template +inline std::vector> get_intersections_with_line(const std::vector &lines, + const TreeType &tree, + const LineType &line) +{ + if (tree.empty()) { + return {}; + } + auto line_bb = typename TreeType::BoundingBox(line.a, line.a); + line_bb.extend(line.b); + + auto intersections = detail::get_intersections_with_line(0, tree, lines, line, line_bb); + if (sorted) { + using Floating = + typename std::conditional::value, typename LineType::Scalar, double>::type; + + std::vector>> points_with_sq_distance{}; + for (const auto &p : intersections) { + points_with_sq_distance.emplace_back((p.first - line.a).template cast().squaredNorm(), p); + } + std::sort(points_with_sq_distance.begin(), points_with_sq_distance.end(), + [](const std::pair> &left, + std::pair> &right) { return left.first < right.first; }); + for (size_t i = 0; i < points_with_sq_distance.size(); i++) { + intersections[i] = points_with_sq_distance[i].second; + } + } + + return intersections; +} + +template class LinesDistancer +{ +public: + using Scalar = typename LineType::Scalar; + using Floating = typename std::conditional::value, Scalar, double>::type; +private: + std::vector lines; + AABBTreeIndirect::Tree<2, Scalar> tree; + +public: + explicit LinesDistancer(const std::vector &lines) : lines(lines) + { + tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(this->lines); + } + + explicit LinesDistancer(std::vector &&lines) : lines(lines) + { + tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(this->lines); + } + + LinesDistancer() = default; + + // 1 true, -1 false, 0 cannot determine + int outside(const Vec<2, Scalar> &point) const { return point_outside_closed_contours(lines, tree, point); } + + // negative sign means inside + template + std::tuple> distance_from_lines_extra(const Vec<2, Scalar> &point) const + { + size_t nearest_line_index_out = size_t(-1); + Vec<2, Floating> nearest_point_out = Vec<2, Floating>::Zero(); + Vec<2, Floating> p = point.template cast(); + auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, nearest_line_index_out, nearest_point_out); + + if (distance < 0) { + return {std::numeric_limits::infinity(), nearest_line_index_out, nearest_point_out}; + } + distance = sqrt(distance); + + if (SIGNED_DISTANCE) { + distance *= outside(point); + } + + return {distance, nearest_line_index_out, nearest_point_out}; + } + + template Floating distance_from_lines(const Vec<2, typename LineType::Scalar> &point) const + { + auto [dist, idx, np] = distance_from_lines_extra(point); + return dist; + } + + std::vector all_lines_in_radius(const Vec<2, typename LineType::Scalar> &point, Floating radius) + { + return all_lines_in_radius(this->lines, this->tree, point, radius * radius); + } + + template std::vector, size_t>> intersections_with_line(const LineType &line) const + { + return get_intersections_with_line>(lines, tree, line); + } + + const LineType &get_line(size_t line_idx) const { return lines[line_idx]; } + + const std::vector &get_lines() const { return lines; } +}; + +}} // namespace Slic3r::AABBTreeLines + #endif /* SRC_LIBSLIC3R_AABBTREELINES_HPP_ */ diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 332702e6c..43d10b5a2 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -22,6 +22,8 @@ set(lisbslic3r_sources ArcFitter.hpp pchheader.cpp pchheader.hpp + AABBTreeIndirect.hpp + AABBTreeLines.hpp BoundingBox.cpp BoundingBox.hpp BridgeDetector.cpp @@ -125,6 +127,8 @@ set(lisbslic3r_sources # GCode/PressureEqualizer.hpp GCode/PrintExtents.cpp GCode/PrintExtents.hpp + GCode/RetractWhenCrossingPerimeters.cpp + GCode/RetractWhenCrossingPerimeters.hpp GCode/SpiralVase.cpp GCode/SpiralVase.hpp GCode/SeamPlacer.cpp diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 0745bea8d..19bd2850e 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -833,6 +833,8 @@ Polylines _clipper_pl_closed(ClipperLib::ClipType clipType, PathProvider1 &&subj return retval; } +Slic3r::Polylines diff_pl(const Slic3r::Polyline& subject, const Slic3r::Polygons& clip) + { return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::PolygonsProvider(clip)); } Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip) { return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::PolygonsProvider(clip)); } Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 0c703b631..d7e0fcc4c 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -430,6 +430,7 @@ Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPoly Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +Slic3r::Polylines diff_pl(const Slic3r::Polyline& subject, const Slic3r::Polygons& clip); Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip); Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip); Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index cb068b8a5..279eeae57 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -4064,7 +4064,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role, LiftTyp } //BBS: need retract when long moving to print perimeter to avoid dropping of material if (!is_perimeter(role) && m_config.reduce_infill_retraction && m_layer != nullptr && - m_config.sparse_infill_density.value > 0 && m_layer->any_internal_region_slmplify_slice_contains(travel)) + m_config.sparse_infill_density.value > 0 && m_retract_when_crossing_perimeters.travel_inside_internal_regions(*m_layer, travel)) // Skip retraction if travel is contained in an internal slice *and* // internal infill is enabled (so that stringing is entirely not visible). //FIXME any_internal_region_slice_contains() is potentionally very slow, it shall test for the bounding boxes first. diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index b7132bbc3..c16b58bbe 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -10,6 +10,7 @@ #include "PrintConfig.hpp" #include "GCode/AvoidCrossingPerimeters.hpp" #include "GCode/CoolingBuffer.hpp" +#include "GCode/RetractWhenCrossingPerimeters.hpp" #include "GCode/SpiralVase.hpp" #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" @@ -423,6 +424,7 @@ private: OozePrevention m_ooze_prevention; Wipe m_wipe; AvoidCrossingPerimeters m_avoid_crossing_perimeters; + RetractWhenCrossingPerimeters m_retract_when_crossing_perimeters; bool m_enable_loop_clipping; // If enabled, the G-code generator will put following comments at the ends // of the G-code lines: _EXTRUDE_SET_SPEED, _WIPE, _OVERHANG_FAN_START, _OVERHANG_FAN_END diff --git a/src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp b/src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp new file mode 100644 index 000000000..3d3b54d45 --- /dev/null +++ b/src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp @@ -0,0 +1,54 @@ +#include "../ClipperUtils.hpp" +#include "../Layer.hpp" +#include "../Polyline.hpp" + +#include "RetractWhenCrossingPerimeters.hpp" + +namespace Slic3r { + +bool RetractWhenCrossingPerimeters::travel_inside_internal_regions(const Layer &layer, const Polyline &travel) +{ + if (m_layer != &layer) { + // Update cache. + m_layer = &layer; + m_internal_islands.clear(); + m_aabbtree_internal_islands.clear(); + // Collect expolygons of internal slices. + for (const LayerRegion *layerm : layer.regions()) + for (const Surface &surface : layerm->get_slices_simplified().surfaces) + if (surface.is_internal()) + m_internal_islands.emplace_back(&surface.expolygon); + // Calculate bounding boxes of internal slices. + std::vector bboxes; + bboxes.reserve(m_internal_islands.size()); + for (size_t i = 0; i < m_internal_islands.size(); ++ i) + bboxes.emplace_back(i, get_extents(*m_internal_islands[i])); + // Build AABB tree over bounding boxes of internal slices. + m_aabbtree_internal_islands.build_modify_input(bboxes); + } + + BoundingBox bbox_travel = get_extents(travel); + AABBTree::BoundingBox bbox_travel_eigen{ bbox_travel.min, bbox_travel.max }; + int result = -1; + bbox_travel.offset(SCALED_EPSILON); + AABBTreeIndirect::traverse(m_aabbtree_internal_islands, + [&bbox_travel_eigen](const AABBTree::Node &node) { + return bbox_travel_eigen.intersects(node.bbox); + }, + [&travel, &bbox_travel, &result, &islands = m_internal_islands](const AABBTree::Node &node) { + assert(node.is_leaf()); + assert(node.is_valid()); + Polygons clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*islands[node.idx], bbox_travel); + if (diff_pl(travel, clipped).empty()) { + // Travel path is completely inside an "internal" island. Don't retract. + result = int(node.idx); + // Stop traversal. + return false; + } + // Continue traversal. + return true; + }); + return result != -1; +} + +} // namespace Slic3r diff --git a/src/libslic3r/GCode/RetractWhenCrossingPerimeters.hpp b/src/libslic3r/GCode/RetractWhenCrossingPerimeters.hpp new file mode 100644 index 000000000..fb624d7f9 --- /dev/null +++ b/src/libslic3r/GCode/RetractWhenCrossingPerimeters.hpp @@ -0,0 +1,32 @@ +#ifndef slic3r_RetractWhenCrossingPerimeters_hpp_ +#define slic3r_RetractWhenCrossingPerimeters_hpp_ + +#include + +#include "../AABBTreeIndirect.hpp" + +namespace Slic3r { + +// Forward declarations. +class ExPolygon; +class Layer; +class Polyline; + +class RetractWhenCrossingPerimeters +{ +public: + bool travel_inside_internal_regions(const Layer &layer, const Polyline &travel); + +private: + // Last object layer visited, for which a cache of internal islands was created. + const Layer *m_layer; + // Internal islands only, referencing data owned by m_layer->regions()->surfaces(). + std::vector m_internal_islands; + // Search structure over internal islands. + using AABBTree = AABBTreeIndirect::Tree<2, coord_t>; + AABBTree m_aabbtree_internal_islands; +}; + +} // namespace Slic3r + +#endif // slic3r_RetractWhenCrossingPerimeters_hpp_ diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 305016a78..0db7fc58c 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -30,6 +30,9 @@ public: const Layer* layer() const { return m_layer; } const PrintRegion& region() const { return *m_region; } + const SurfaceCollection& get_slices() const { return slices; } + const SurfaceCollection& get_slices_simplified() const { return slices_simplified; } + // collection of surfaces generated by slicing the original geometry // divided by type top/bottom/internal SurfaceCollection slices; @@ -170,11 +173,6 @@ public: for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_internal_contains(item)) return true; return false; } - //BBS: only be used in gcode export when reduce_infill_retraction is enabled - template bool any_internal_region_slmplify_slice_contains(const T& item) const { - for (const LayerRegion* layerm : m_regions) if (layerm->slices_simplified.any_internal_contains(item)) return true; - return false; - } template bool any_bottom_region_slice_contains(const T &item) const { for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_bottom_contains(item)) return true; return false; diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 7dae5072e..ba6fb7032 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -335,6 +335,7 @@ remove_unconnected_vertices(const indexed_triangle_set &its) // Drill holes into the hollowed/original mesh. void SLAPrint::Steps::drill_holes(SLAPrintObject &po) { + /* bool needs_drilling = ! po.m_model_object->sla_drain_holes.empty(); bool is_hollowed = (po.m_hollowing_data && po.m_hollowing_data->interior && @@ -465,6 +466,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) if (hole_fail) po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, L("Failed to drill some holes into the model")); + */ } // The slicing will be performed on an imaginary 1D grid which starts from