Reworked "only_retract_when_crossing_perimeters" feature, which

was terribly slow:
Introduced RetractWhenCrossingPerimeters.cpp,hpp
AABBTreeIndirect traverse template was extended to support early exit.
This commit is contained in:
Vojtech Bubnik 2022-12-05 09:59:59 +01:00
parent b1bfef44ba
commit 7309c729e0
9 changed files with 140 additions and 9 deletions

View File

@ -920,29 +920,35 @@ template<class G> auto within(const G &g) { return Within<G>{g}; }
namespace detail { namespace detail {
// Returns true in case traversal should continue,
// returns false if traversal should stop (for example if the first hit was found).
template<int Dims, typename T, typename Pred, typename Fn> template<int Dims, typename T, typename Pred, typename Fn>
void traverse_recurse(const Tree<Dims, T> &tree, bool traverse_recurse(const Tree<Dims, T> &tree,
size_t idx, size_t idx,
Pred && pred, Pred && pred,
Fn && callback) Fn && callback)
{ {
assert(tree.node(idx).is_valid()); 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()) { 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 { } else {
// call this with left and right node idx: // call this with left and right node idx:
auto trv = [&](size_t idx) { auto trv = [&](size_t idx) -> bool {
traverse_recurse(tree, idx, std::forward<Pred>(pred), return traverse_recurse(tree, idx, std::forward<Pred>(pred),
std::forward<Fn>(callback)); std::forward<Fn>(callback));
}; };
// Left / right child node index. // Left / right child node index.
trv(Tree<Dims, T>::left_child_idx(idx)); // Returns true if both children allow the traversal to continue.
trv(Tree<Dims, T>::right_child_idx(idx)); return trv(Tree<Dims, T>::left_child_idx(idx)) &&
trv(Tree<Dims, T>::right_child_idx(idx));
} }
} }
@ -952,6 +958,7 @@ void traverse_recurse(const Tree<Dims, T> &tree,
// traverse(tree, intersecting(QueryBox), [](size_t face_idx) { // 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<int Dims, typename T, typename Predicate, typename Fn> template<int Dims, typename T, typename Predicate, typename Fn>
void traverse(const Tree<Dims, T> &tree, Predicate &&pred, Fn &&callback) void traverse(const Tree<Dims, T> &tree, Predicate &&pred, Fn &&callback)
{ {

View File

@ -130,6 +130,8 @@ set(SLIC3R_SOURCES
GCode/PressureEqualizer.hpp GCode/PressureEqualizer.hpp
GCode/PrintExtents.cpp GCode/PrintExtents.cpp
GCode/PrintExtents.hpp GCode/PrintExtents.hpp
GCode/RetractWhenCrossingPerimeters.cpp
GCode/RetractWhenCrossingPerimeters.hpp
GCode/SpiralVase.cpp GCode/SpiralVase.cpp
GCode/SpiralVase.hpp GCode/SpiralVase.hpp
GCode/SeamPlacer.cpp GCode/SeamPlacer.cpp

View File

@ -140,6 +140,21 @@ namespace ClipperUtils {
out.reserve(src.size()); out.reserve(src.size());
for (const Polygon &p : src) for (const Polygon &p : src)
out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox)); 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; return out;
} }
} }
@ -794,6 +809,8 @@ Polylines _clipper_pl_closed(ClipperLib::ClipType clipType, PathProvider1 &&subj
return retval; 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) Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip)
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::PolygonsProvider(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) Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip)

View File

@ -323,6 +323,7 @@ namespace ClipperUtils {
void clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, Polygon &out); 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]] 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 Polygons &src, const BoundingBox &bbox);
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox);
} }
// offset Polygons // 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::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::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::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::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::ExPolygon &clip);
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygons &clip); Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygons &clip);

View File

@ -3090,7 +3090,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
} }
if (m_config.only_retract_when_crossing_perimeters && m_layer != nullptr && 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* // Skip retraction if travel is contained in an internal slice *and*
// internal infill is enabled (so that stringing is entirely not visible). // 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. //FIXME any_internal_region_slice_contains() is potentionally very slow, it shall test for the bounding boxes first.

View File

@ -12,6 +12,7 @@
#include "GCode/AvoidCrossingPerimeters.hpp" #include "GCode/AvoidCrossingPerimeters.hpp"
#include "GCode/CoolingBuffer.hpp" #include "GCode/CoolingBuffer.hpp"
#include "GCode/FindReplace.hpp" #include "GCode/FindReplace.hpp"
#include "GCode/RetractWhenCrossingPerimeters.hpp"
#include "GCode/SpiralVase.hpp" #include "GCode/SpiralVase.hpp"
#include "GCode/ToolOrdering.hpp" #include "GCode/ToolOrdering.hpp"
#include "GCode/WipeTower.hpp" #include "GCode/WipeTower.hpp"
@ -349,6 +350,7 @@ private:
Wipe m_wipe; Wipe m_wipe;
AvoidCrossingPerimeters m_avoid_crossing_perimeters; AvoidCrossingPerimeters m_avoid_crossing_perimeters;
JPSPathFinder m_avoid_curled_filaments; JPSPathFinder m_avoid_curled_filaments;
RetractWhenCrossingPerimeters m_retract_when_crossing_perimeters;
bool m_enable_loop_clipping; bool m_enable_loop_clipping;
// If enabled, the G-code generator will put following comments at the ends // 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 // of the G-code lines: _EXTRUDE_SET_SPEED, _WIPE, _BRIDGE_FAN_START, _BRIDGE_FAN_END

View File

@ -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<int64_t>() + m_bbox.max().cast<int64_t>()) / 2).cast<int32_t>(); }
private:
size_t m_idx;
AABBTree::BoundingBox m_bbox;
};
std::vector<BBoxWrapper> 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

View File

@ -0,0 +1,32 @@
#ifndef slic3r_RetractWhenCrossingPerimeters_hpp_
#define slic3r_RetractWhenCrossingPerimeters_hpp_
#include <vector>
#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<const ExPolygon*> 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_

View File

@ -413,6 +413,8 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
[&part_to_drill, &hollowed_mesh](const auto& node) [&part_to_drill, &hollowed_mesh](const auto& node)
{ {
part_to_drill.indices.emplace_back(hollowed_mesh.its.indices[node.idx]); 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( auto cgal_meshpart = MeshBoolean::cgal::triangle_mesh_to_cgal(