From 7309c729e03d3170bb09ebfc548e19c79a077cc1 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 5 Dec 2022 09:59:59 +0100 Subject: [PATCH] Reworked "only_retract_when_crossing_perimeters" feature, which was terribly slow: Introduced RetractWhenCrossingPerimeters.cpp,hpp AABBTreeIndirect traverse template was extended to support early exit. --- src/libslic3r/AABBTreeIndirect.hpp | 23 ++++--- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/ClipperUtils.cpp | 17 +++++ src/libslic3r/ClipperUtils.hpp | 2 + src/libslic3r/GCode.cpp | 2 +- src/libslic3r/GCode.hpp | 2 + .../GCode/RetractWhenCrossingPerimeters.cpp | 67 +++++++++++++++++++ .../GCode/RetractWhenCrossingPerimeters.hpp | 32 +++++++++ src/libslic3r/SLAPrintSteps.cpp | 2 + 9 files changed, 140 insertions(+), 9 deletions(-) create mode 100644 src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp create mode 100644 src/libslic3r/GCode/RetractWhenCrossingPerimeters.hpp diff --git a/src/libslic3r/AABBTreeIndirect.hpp b/src/libslic3r/AABBTreeIndirect.hpp index b4b74ef13..e70e8be39 100644 --- a/src/libslic3r/AABBTreeIndirect.hpp +++ b/src/libslic3r/AABBTreeIndirect.hpp @@ -920,29 +920,35 @@ 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)); + // 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)); } } @@ -952,6 +958,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/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index bee7ceb62..9afa00ccb 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -130,6 +130,8 @@ set(SLIC3R_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 ea5015798..4762c3e24 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -140,6 +140,21 @@ namespace ClipperUtils { out.reserve(src.size()); for (const Polygon &p : src) out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox)); + out.erase( + std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), + out.end()); + return out; + } + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox) + { + Polygons out; + out.reserve(src.num_contours()); + out.emplace_back(clip_clipper_polygon_with_subject_bbox(src.contour, bbox)); + for (const Polygon &p : src.holes) + out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox)); + out.erase( + std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), + out.end()); return out; } } @@ -794,6 +809,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 4017a73f1..65ecfb76a 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -323,6 +323,7 @@ namespace ClipperUtils { void clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, Polygon &out); [[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox); [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox); + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox); } // offset Polygons @@ -427,6 +428,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::Polyline &subject, const Slic3r::ExPolygons &clip); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 06801b0e4..dc587f388 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3090,7 +3090,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role) } if (m_config.only_retract_when_crossing_perimeters && m_layer != nullptr && - m_config.fill_density.value > 0 && m_layer->any_internal_region_slice_contains(travel)) + m_config.fill_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 435941de5..e37d11a2e 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -12,6 +12,7 @@ #include "GCode/AvoidCrossingPerimeters.hpp" #include "GCode/CoolingBuffer.hpp" #include "GCode/FindReplace.hpp" +#include "GCode/RetractWhenCrossingPerimeters.hpp" #include "GCode/SpiralVase.hpp" #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" @@ -349,6 +350,7 @@ private: Wipe m_wipe; AvoidCrossingPerimeters m_avoid_crossing_perimeters; JPSPathFinder m_avoid_curled_filaments; + 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, _BRIDGE_FAN_START, _BRIDGE_FAN_END diff --git a/src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp b/src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp new file mode 100644 index 000000000..4be42d763 --- /dev/null +++ b/src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp @@ -0,0 +1,67 @@ +#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->slices().surfaces) + if (surface.is_internal()) + m_internal_islands.emplace_back(&surface.expolygon); + // Calculate bounding boxes of internal slices. + class BBoxWrapper { + public: + BBoxWrapper(const size_t idx, const 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 AABBTree::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; + AABBTree::BoundingBox m_bbox; + }; + 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/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 4498cb6b0..bed4f0249 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -413,6 +413,8 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) [&part_to_drill, &hollowed_mesh](const auto& node) { part_to_drill.indices.emplace_back(hollowed_mesh.its.indices[node.idx]); + // continue traversal + return true; }); auto cgal_meshpart = MeshBoolean::cgal::triangle_mesh_to_cgal(